[med-svn] [relion] 01/10: Imported Upstream version 1.4+dfsg

Roland Fehrenbacher rfehren-guest at moszumanska.debian.org
Tue Dec 8 18:57:51 UTC 2015


This is an automated email from the git hooks/post-receive script.

rfehren-guest pushed a commit to branch master
in repository relion.

commit a3664543b5886b8c4cb6861b154c2e8c0ece438a
Author: Roland Fehrenbacher <rf at q-leap.de>
Date:   Mon Oct 26 16:50:28 2015 +0000

    Imported Upstream version 1.4+dfsg
---
 INSTALL.sh                       |   23 +-
 Makefile.am                      |   34 +-
 Makefile.in                      |   60 +-
 autogen.sh                       |    4 +-
 config.h.in                      |    6 +
 configure                        |  268 +-
 configure.ac                     |   50 +-
 scripts/qsub.csh                 |    5 +-
 src/apps/autopick_mpi.cpp        |    5 +-
 src/apps/find_tiltpairs.cpp      |   74 +-
 src/apps/image_handler.cpp       |  605 ++--
 src/apps/maingui.cpp             |   13 +-
 src/apps/mask_create.cpp         |    4 +-
 src/apps/particle_polish_mpi.cpp |    5 +-
 src/apps/particle_sort_mpi.cpp   |    5 +-
 src/apps/preprocess_mpi.cpp      |    2 +-
 src/apps/project.cpp             |  131 +-
 src/apps/reconstruct.cpp         |  122 +-
 src/apps/refine_mpi.cpp          |    7 +-
 src/apps/run_ctffind_mpi.cpp     |    5 +-
 src/apps/stack_create.cpp        |   14 +-
 src/apps/star_compare.cpp        |  109 +
 src/apps/tiltpair_plot.cpp       |  329 +++
 src/assembly.cpp                 |    6 +-
 src/assembly.h                   |   12 +-
 src/autopicker.cpp               |  104 +-
 src/autopicker.h                 |   32 +-
 src/backprojector.cpp            |  375 ++-
 src/backprojector.h              |  100 +-
 src/complex.cpp                  |   34 +-
 src/complex.h                    |   33 +-
 src/ctf.cpp                      |   28 +-
 src/ctf.h                        |   78 +-
 src/ctffind_runner.cpp           |   50 +-
 src/ctffind_runner.h             |   36 +-
 src/ctffind_runner_mpi.cpp       |    4 +-
 src/displayer.cpp                |  113 +-
 src/displayer.h                  |   56 +-
 src/euler.cpp                    |  101 +-
 src/euler.h                      |  106 +-
 src/exp_model.cpp                |  852 +++---
 src/exp_model.h                  |  115 +-
 src/fftw.cpp                     |  424 +--
 src/fftw.h                       |  106 +-
 src/filename.cpp                 |   29 +-
 src/funcs.cpp                    |  273 +-
 src/funcs.h                      |  129 +-
 src/gcc_version.h                |    1 -
 src/gui_entries.h                |    8 +-
 src/gui_jobwindow.cpp            |  165 +-
 src/gui_jobwindow.h              |   39 +-
 src/gui_mainwindow.cpp           |   39 +-
 src/healpix_sampling.cpp         |  928 +++---
 src/healpix_sampling.h           |  139 +-
 src/image.cpp                    |   89 +-
 src/image.h                      |   42 +-
 src/macros.h                     |   71 +-
 src/manualpicker.cpp             |   29 +-
 src/mask.cpp                     |   42 +-
 src/mask.h                       |   10 +-
 src/matrix1d.cpp                 |   16 +-
 src/matrix1d.h                   |  105 +-
 src/matrix2d.cpp                 |    4 +-
 src/matrix2d.h                   |  168 +-
 src/metadata_container.cpp       |  120 +-
 src/metadata_container.h         |   27 +-
 src/metadata_label.cpp           |    2 +-
 src/metadata_label.h             |   10 +-
 src/metadata_table.cpp           |  263 +-
 src/metadata_table.h             |   22 +-
 src/ml_model.cpp                 |  136 +-
 src/ml_model.h                   |   77 +-
 src/ml_optimiser.cpp             | 5730 ++++++++++++++++++++------------------
 src/ml_optimiser.h               |  235 +-
 src/ml_optimiser_mpi.cpp         |  362 ++-
 src/ml_optimiser_mpi.h           |    5 +-
 src/mpi.cpp                      |  216 +-
 src/mpi.h                        |   10 +-
 src/multidim_array.h             |  241 +-
 src/numerical_recipes.cpp        |  265 +-
 src/numerical_recipes.h          |   53 +-
 src/parallel.cpp                 |    1 +
 src/particle_polisher.cpp        |  389 ++-
 src/particle_polisher.h          |   41 +-
 src/particle_polisher_mpi.cpp    |   42 +-
 src/particle_sorter.cpp          |   96 +-
 src/particle_sorter.h            |   18 +-
 src/particle_sorter_mpi.cpp      |    4 +-
 src/postprocessing.cpp           |   96 +-
 src/postprocessing.h             |   26 +-
 src/preprocessing.cpp            |   77 +-
 src/preprocessing.h              |   13 +-
 src/projector.cpp                |  220 +-
 src/projector.h                  |   52 +-
 src/rwIMAGIC.h                   |   22 +-
 src/rwMRC.h                      |   42 +-
 src/rwSPIDER.h                   |   22 +-
 src/strings.cpp                  |   10 +-
 src/strings.h                    |    9 +-
 src/symmetries.cpp               |   38 +-
 src/symmetries.h                 |   12 +-
 src/tabfuncs.cpp                 |   30 +-
 src/tabfuncs.h                   |   24 +-
 src/transformations.cpp          |   29 +-
 src/transformations.h            |   98 +-
 105 files changed, 9013 insertions(+), 7143 deletions(-)

diff --git a/INSTALL.sh b/INSTALL.sh
index 481a2f5..2186933 100755
--- a/INSTALL.sh
+++ b/INSTALL.sh
@@ -6,14 +6,14 @@ BUILD_FLTK=true
 BUILD_RELION=true
 N_THREADS=$@
 
+# Use single-precision instead of the default of double-precision?
+FLOAT_PRECISION=false
+
 # do we have mpi and fltk?
-# Set the param below to "false" if you do not have an MPI installation and only want to build the sequential version of RELION
 HAVE_MPI=true
-# Set the param below to "false" if you have trouble compiling fltk and still want to build RELION without the GUI
 HAVE_FLTK=true
 
 #Some path variables
-# Note that as of RELION-1.3, the prefix actually needs to be RELION_HOME. You can move the bin and lib directories elsewhere after building
 RELION_HOME=$PWD
 PREFIX=$RELION_HOME
 
@@ -35,7 +35,12 @@ if $BUILD_FFTW; then
   cd external
   tar -zxf $VFFTW.tar.gz
   cd $VFFTW
-  ./configure --enable-threads --enable-shared prefix=$PREFIX > $RELION_HOME/external/fftw_build.log
+  if $FLOAT_PRECISION; then
+   float_option=" --enable-float "
+  else
+   float_option=""
+  fi
+   ./configure $float_option --enable-threads --enable-shared prefix=$PREFIX > $RELION_HOME/external/fftw_build.log
   make $N_THREADS >> $RELION_HOME/external/fftw_build.log 
   make install >> $RELION_HOME/external/fftw_build.log 
   cd ../..
@@ -65,11 +70,17 @@ if $BUILD_RELION; then
   fltk_cxx=""
   fltk_ld=""
  fi
+ if $FLOAT_PRECISION; then
+  float_option=" --enable-float "
+ else
+  float_option=""
+ fi
  if $HAVE_MPI; then
-  ./configure prefix=$PREFIX --enable-mpi CPPFLAGS="-I$PREFIX/include $fltk_cxx"  LDFLAGS="-L$PREFIX/lib $fltk_ld" > $RELION_HOME/relion_build.log
+  mpi_option=" --enable-mpi "
  else
-  ./configure prefix=$PREFIX CPPFLAGS="-I$PREFIX/include $fltk_cxx"  LDFLAGS="-L$PREFIX/lib $fltk_ld" > $RELION_HOME/relion_build.log
+  mpi_option=""
  fi
+ ./configure $mpi_option $float_option prefix=$PREFIX CPPFLAGS="-I$PREFIX/include $fltk_cxx"  LDFLAGS="-L$PREFIX/lib $fltk_ld" > $RELION_HOME/relion_build.log
  make $N_THREADS >> $RELION_HOME/relion_build.log
  make install >> $RELION_HOME/relion_build.log
  mv $RELION_HOME/bin/relion_maingui $PREFIX/bin/relion 
diff --git a/Makefile.am b/Makefile.am
index 9897ebb..381c6ec 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -212,9 +212,9 @@ pkgconfig_DATA = relion-$(RELION_API_VERSION).pc
 #
 ###############################################################################
 
-bin_PROGRAMS = project reconstruct refine preprocess postprocess autopick particle_polish particle_sort run_ctffind
+bin_PROGRAMS = project reconstruct refine preprocess postprocess autopick particle_polish particle_sort run_ctffind star_compare find_tiltpairs tiltpair_plot
 if HAVE_MPI
- bin_PROGRAMS += refine_mpi preprocess_mpi autopick_mpi particle_polish_mpi particle_sort_mpi run_ctffind_mpi
+ bin_PROGRAMS += refine_mpi preprocess_mpi autopick_mpi particle_polish_mpi particle_sort_mpi run_ctffind_mpi 
 endif
 if HAVE_FLTK
  bin_PROGRAMS += manualpick display maingui 	
@@ -270,6 +270,9 @@ run_ctffind_LDADD = $(RelionLibs)
 run_ctffind_mpi_SOURCES = src/apps/run_ctffind_mpi.cpp
 run_ctffind_mpi_LDADD = $(RelionLibs)
 
+star_compare_SOURCES = src/apps/star_compare.cpp
+star_compare_LDADD = $(RelionLibs)
+
 display_SOURCES = src/apps/display.cpp
 display_LDADD = $(RelionLibs)
 
@@ -292,25 +295,16 @@ bin_PROGRAMS += find_tiltpairs
 find_tiltpairs_SOURCES = src/apps/find_tiltpairs.cpp
 find_tiltpairs_LDADD = $(RelionLibs)
 
-#bin_PROGRAMS += denovo_model
-#denovo_model_SOURCES = src/apps/denovo_model.cpp
-#denovo_model_LDADD = $(RelionLibs)
-
-#bin_PROGRAMS += tester
-#tester_SOURCES = src/apps/tester.cpp
-#tester_LDADD = $(RelionLibs)
-
-#bin_PROGRAMS += fsc2bfac 
-#fsc2bfac_SOURCES = src/apps/fsc2bfac.cpp
-#fsc2bfac_LDADD = $(RelionLibs)
-
-#bin_PROGRAMS += convert_bild2map
-#convert_bild2map_SOURCES = src/apps/convert_bild2map.cpp
-#convert_bild2map_LDADD = $(RelionLibs)
+#bin_PROGRAMS += subtomo_proj3d_correct
+#subtomo_proj3d_correct_SOURCES = src/apps/subtomo_proj3d_correct.cpp
+#subtomo_proj3d_correct_LDADD = $(RelionLibs)
 
-#bin_PROGRAMS += composite_map
-#composite_map_SOURCES = src/apps/composite_map.cpp
-#composite_map_LDADD = $(RelionLibs)
+#bin_PROGRAMS += subtomo_angle_converter
+#subtomo_angle_converter_SOURCES = src/apps/subtomo_angle_converter.cpp
+#subtomo_angle_converter_LDADD = $(RelionLibs)
 
+bin_PROGRAMS += tiltpair_plot
+tiltpair_plot_SOURCES = src/apps/tiltpair_plot.cpp
+tiltpair_plot_LDADD = $(RelionLibs)
 
 
diff --git a/Makefile.in b/Makefile.in
index 4227e8d..4865f8a 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -69,10 +69,12 @@ host_triplet = @host@
 bin_PROGRAMS = project$(EXEEXT) reconstruct$(EXEEXT) refine$(EXEEXT) \
 	preprocess$(EXEEXT) postprocess$(EXEEXT) autopick$(EXEEXT) \
 	particle_polish$(EXEEXT) particle_sort$(EXEEXT) \
-	run_ctffind$(EXEEXT) $(am__EXEEXT_1) $(am__EXEEXT_2) \
-	mask_create$(EXEEXT) stack_create$(EXEEXT) \
-	image_handler$(EXEEXT) find_tiltpairs$(EXEEXT)
- at HAVE_MPI_TRUE@am__append_5 = refine_mpi preprocess_mpi autopick_mpi particle_polish_mpi particle_sort_mpi run_ctffind_mpi
+	run_ctffind$(EXEEXT) star_compare$(EXEEXT) \
+	find_tiltpairs$(EXEEXT) tiltpair_plot$(EXEEXT) $(am__EXEEXT_1) \
+	$(am__EXEEXT_2) mask_create$(EXEEXT) stack_create$(EXEEXT) \
+	image_handler$(EXEEXT) find_tiltpairs$(EXEEXT) \
+	tiltpair_plot$(EXEEXT)
+ at HAVE_MPI_TRUE@am__append_5 = refine_mpi preprocess_mpi autopick_mpi particle_polish_mpi particle_sort_mpi run_ctffind_mpi 
 @HAVE_FLTK_TRUE at am__append_6 = manualpick display maingui 	
 subdir = .
 DIST_COMMON = README $(am__configure_deps) \
@@ -245,6 +247,12 @@ run_ctffind_mpi_DEPENDENCIES = $(RelionLibs)
 am_stack_create_OBJECTS = src/apps/stack_create.$(OBJEXT)
 stack_create_OBJECTS = $(am_stack_create_OBJECTS)
 stack_create_DEPENDENCIES = $(RelionLibs)
+am_star_compare_OBJECTS = src/apps/star_compare.$(OBJEXT)
+star_compare_OBJECTS = $(am_star_compare_OBJECTS)
+star_compare_DEPENDENCIES = $(RelionLibs)
+am_tiltpair_plot_OBJECTS = src/apps/tiltpair_plot.$(OBJEXT)
+tiltpair_plot_OBJECTS = $(am_tiltpair_plot_OBJECTS)
+tiltpair_plot_DEPENDENCIES = $(RelionLibs)
 SCRIPTS = $(dist_bin_SCRIPTS) $(dist_noinst_SCRIPTS)
 DEFAULT_INCLUDES = -I. at am__isrc@
 depcomp = $(SHELL) $(top_srcdir)/depcomp
@@ -269,7 +277,8 @@ SOURCES = $(librelion_ at RELION_API_VERSION@_la_SOURCES) \
 	$(preprocess_SOURCES) $(preprocess_mpi_SOURCES) \
 	$(project_SOURCES) $(reconstruct_SOURCES) $(refine_SOURCES) \
 	$(refine_mpi_SOURCES) $(run_ctffind_SOURCES) \
-	$(run_ctffind_mpi_SOURCES) $(stack_create_SOURCES)
+	$(run_ctffind_mpi_SOURCES) $(stack_create_SOURCES) \
+	$(star_compare_SOURCES) $(tiltpair_plot_SOURCES)
 DIST_SOURCES = $(am__librelion_ at RELION_API_VERSION@_la_SOURCES_DIST) \
 	$(autopick_SOURCES) $(autopick_mpi_SOURCES) $(display_SOURCES) \
 	$(find_tiltpairs_SOURCES) $(image_handler_SOURCES) \
@@ -280,7 +289,8 @@ DIST_SOURCES = $(am__librelion_ at RELION_API_VERSION@_la_SOURCES_DIST) \
 	$(preprocess_SOURCES) $(preprocess_mpi_SOURCES) \
 	$(project_SOURCES) $(reconstruct_SOURCES) $(refine_SOURCES) \
 	$(refine_mpi_SOURCES) $(run_ctffind_SOURCES) \
-	$(run_ctffind_mpi_SOURCES) $(stack_create_SOURCES)
+	$(run_ctffind_mpi_SOURCES) $(stack_create_SOURCES) \
+	$(star_compare_SOURCES) $(tiltpair_plot_SOURCES)
 DATA = $(pkgconfig_DATA)
 am__nobase_relion_include_HEADERS_DIST = relion.h src/args.h \
 	src/assembly.h src/autopicker.h src/backprojector.h \
@@ -544,6 +554,8 @@ run_ctffind_SOURCES = src/apps/run_ctffind.cpp
 run_ctffind_LDADD = $(RelionLibs)
 run_ctffind_mpi_SOURCES = src/apps/run_ctffind_mpi.cpp
 run_ctffind_mpi_LDADD = $(RelionLibs)
+star_compare_SOURCES = src/apps/star_compare.cpp
+star_compare_LDADD = $(RelionLibs)
 display_SOURCES = src/apps/display.cpp
 display_LDADD = $(RelionLibs)
 maingui_SOURCES = src/apps/maingui.cpp
@@ -556,6 +568,8 @@ image_handler_SOURCES = src/apps/image_handler.cpp
 image_handler_LDADD = $(RelionLibs)
 find_tiltpairs_SOURCES = src/apps/find_tiltpairs.cpp
 find_tiltpairs_LDADD = $(RelionLibs)
+tiltpair_plot_SOURCES = src/apps/tiltpair_plot.cpp
+tiltpair_plot_LDADD = $(RelionLibs)
 all: config.h
 	$(MAKE) $(AM_MAKEFLAGS) all-am
 
@@ -892,6 +906,16 @@ src/apps/stack_create.$(OBJEXT): src/apps/$(am__dirstamp) \
 stack_create$(EXEEXT): $(stack_create_OBJECTS) $(stack_create_DEPENDENCIES) 
 	@rm -f stack_create$(EXEEXT)
 	$(CXXLINK) $(stack_create_OBJECTS) $(stack_create_LDADD) $(LIBS)
+src/apps/star_compare.$(OBJEXT): src/apps/$(am__dirstamp) \
+	src/apps/$(DEPDIR)/$(am__dirstamp)
+star_compare$(EXEEXT): $(star_compare_OBJECTS) $(star_compare_DEPENDENCIES) 
+	@rm -f star_compare$(EXEEXT)
+	$(CXXLINK) $(star_compare_OBJECTS) $(star_compare_LDADD) $(LIBS)
+src/apps/tiltpair_plot.$(OBJEXT): src/apps/$(am__dirstamp) \
+	src/apps/$(DEPDIR)/$(am__dirstamp)
+tiltpair_plot$(EXEEXT): $(tiltpair_plot_OBJECTS) $(tiltpair_plot_DEPENDENCIES) 
+	@rm -f tiltpair_plot$(EXEEXT)
+	$(CXXLINK) $(tiltpair_plot_OBJECTS) $(tiltpair_plot_LDADD) $(LIBS)
 install-dist_binSCRIPTS: $(dist_bin_SCRIPTS)
 	@$(NORMAL_INSTALL)
 	test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
@@ -955,6 +979,8 @@ mostlyclean-compile:
 	-rm -f src/apps/run_ctffind.$(OBJEXT)
 	-rm -f src/apps/run_ctffind_mpi.$(OBJEXT)
 	-rm -f src/apps/stack_create.$(OBJEXT)
+	-rm -f src/apps/star_compare.$(OBJEXT)
+	-rm -f src/apps/tiltpair_plot.$(OBJEXT)
 	-rm -f src/args.$(OBJEXT)
 	-rm -f src/args.lo
 	-rm -f src/assembly.$(OBJEXT)
@@ -1130,6 +1156,8 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote at src/apps/$(DEPDIR)/run_ctffind.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at src/apps/$(DEPDIR)/run_ctffind_mpi.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at src/apps/$(DEPDIR)/stack_create.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/apps/$(DEPDIR)/star_compare.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at src/apps/$(DEPDIR)/tiltpair_plot.Po at am__quote@
 
 .cc.o:
 @am__fastdepCXX_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
@@ -1576,26 +1604,6 @@ uninstall-am: uninstall-binPROGRAMS uninstall-dist_binSCRIPTS \
 	uninstall-nobase_relion_includeHEADERS uninstall-pkgconfigDATA
 
 
-#bin_PROGRAMS += denovo_model
-#denovo_model_SOURCES = src/apps/denovo_model.cpp
-#denovo_model_LDADD = $(RelionLibs)
-
-#bin_PROGRAMS += tester
-#tester_SOURCES = src/apps/tester.cpp
-#tester_LDADD = $(RelionLibs)
-
-#bin_PROGRAMS += fsc2bfac 
-#fsc2bfac_SOURCES = src/apps/fsc2bfac.cpp
-#fsc2bfac_LDADD = $(RelionLibs)
-
-#bin_PROGRAMS += convert_bild2map
-#convert_bild2map_SOURCES = src/apps/convert_bild2map.cpp
-#convert_bild2map_LDADD = $(RelionLibs)
-
-#bin_PROGRAMS += composite_map
-#composite_map_SOURCES = src/apps/composite_map.cpp
-#composite_map_LDADD = $(RelionLibs)
-
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
 .NOEXPORT:
diff --git a/autogen.sh b/autogen.sh
index d20be9a..af6ae6d 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -2,7 +2,7 @@
 setenv RELION_HOME $PWD
 setenv PREFIX $RELION_HOME
 autoreconf --install
-fltk_cxx=`fltk-config --cxxflags`
-fltk_ld=`fltk-config --ldflags`
+setenv fltk_cxx `fltk-config --cxxflags`
+setenv fltk_ld `fltk-config --ldflags`
  ./configure --enable-mpi CPPFLAGS="-I/lmb/home/scheres/app/fftw-3.2.2/include -I$PREFIX/include $fltk_cxx" LDFLAGS="-L/lmb/home/scheres/app/fftw-3.2.2/lib -L$PREFIX/lib $fltk_ld" 
 
diff --git a/config.h.in b/config.h.in
index efb8a41..9d11484 100644
--- a/config.h.in
+++ b/config.h.in
@@ -9,6 +9,12 @@
 /* Define to 1 if you have the `fftw3' library (-lfftw3). */
 #undef HAVE_LIBFFTW3
 
+/* Define to 1 if you have the `fftw3f' library (-lfftw3f). */
+#undef HAVE_LIBFFTW3F
+
+/* Define to 1 if you have the `fftw3f_threads' library (-lfftw3f_threads). */
+#undef HAVE_LIBFFTW3F_THREADS
+
 /* Define to 1 if you have the `fftw3_threads' library (-lfftw3_threads). */
 #undef HAVE_LIBFFTW3_THREADS
 
diff --git a/configure b/configure
index 754a495..ca4bd6f 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.63 for relion 1.3.
+# Generated by GNU Autoconf 2.63 for relion 1.4.
 #
 # Report bugs to <scheres at mrc-lmb.cam.ac.uk>.
 #
@@ -745,8 +745,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
 # Identity of this package.
 PACKAGE_NAME='relion'
 PACKAGE_TARNAME='relion'
-PACKAGE_VERSION='1.3'
-PACKAGE_STRING='relion 1.3'
+PACKAGE_VERSION='1.4'
+PACKAGE_STRING='relion 1.4'
 PACKAGE_BUGREPORT='scheres at mrc-lmb.cam.ac.uk'
 
 # Factoring default headers for most tests.
@@ -792,6 +792,8 @@ LTLIBOBJS
 LIBOBJS
 HAVE_FLTK_FALSE
 HAVE_FLTK_TRUE
+FLOAT_PRECISION_FALSE
+FLOAT_PRECISION_TRUE
 HAVE_MPI_FALSE
 HAVE_MPI_TRUE
 MPICXX
@@ -919,6 +921,7 @@ enable_fast_install
 with_gnu_ld
 enable_libtool_lock
 enable_mpi
+enable_float
 '
       ac_precious_vars='build_alias
 host_alias
@@ -1485,7 +1488,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures relion 1.3 to adapt to many kinds of systems.
+\`configure' configures relion 1.4 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1555,7 +1558,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of relion 1.3:";;
+     short | recursive ) echo "Configuration of relion 1.4:";;
    esac
   cat <<\_ACEOF
 
@@ -1571,6 +1574,7 @@ Optional Features:
                           optimize for fast installation [default=yes]
   --disable-libtool-lock  avoid locking (might break parallel builds)
   --enable-mpi            compile with MPI support
+  --enable-float          compile with float-precision
 
 Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
@@ -1658,7 +1662,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-relion configure 1.3
+relion configure 1.4
 generated by GNU Autoconf 2.63
 
 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1672,7 +1676,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by relion $as_me 1.3, which was
+It was created by relion $as_me 1.4, which was
 generated by GNU Autoconf 2.63.  Invocation command line was
 
   $ $0 $@
@@ -2521,7 +2525,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='relion'
- VERSION='1.3'
+ VERSION='1.4'
 
 
 # Some tools Automake needs.
@@ -2557,9 +2561,9 @@ am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
 # For information on how to properly maintain the library version information,
 # refer to the libtool manual, section "Updating library version information":
 # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
-RELION_SO_VERSION=1:3:0
+RELION_SO_VERSION=1:4:0
 
-RELION_API_VERSION=1.3
+RELION_API_VERSION=1.4
 
 
 # Generate two configuration headers; one for building the library itself with
@@ -5020,13 +5024,13 @@ if test "${lt_cv_nm_interface+set}" = set; then
 else
   lt_cv_nm_interface="BSD nm"
   echo "int some_variable = 0;" > conftest.$ac_ext
-  (eval echo "\"\$as_me:5023: $ac_compile\"" >&5)
+  (eval echo "\"\$as_me:5027: $ac_compile\"" >&5)
   (eval "$ac_compile" 2>conftest.err)
   cat conftest.err >&5
-  (eval echo "\"\$as_me:5026: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+  (eval echo "\"\$as_me:5030: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
   (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
   cat conftest.err >&5
-  (eval echo "\"\$as_me:5029: output\"" >&5)
+  (eval echo "\"\$as_me:5033: output\"" >&5)
   cat conftest.out >&5
   if $GREP 'External.*some_variable' conftest.out > /dev/null; then
     lt_cv_nm_interface="MS dumpbin"
@@ -6232,7 +6236,7 @@ ia64-*-hpux*)
   ;;
 *-*-irix6*)
   # Find out which ABI we are using.
-  echo '#line 6235 "configure"' > conftest.$ac_ext
+  echo '#line 6239 "configure"' > conftest.$ac_ext
   if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
   (eval $ac_compile) 2>&5
   ac_status=$?
@@ -8823,11 +8827,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:8826: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:8830: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:8830: \$? = $ac_status" >&5
+   echo "$as_me:8834: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -9162,11 +9166,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:9165: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:9169: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:9169: \$? = $ac_status" >&5
+   echo "$as_me:9173: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -9267,11 +9271,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:9270: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:9274: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:9274: \$? = $ac_status" >&5
+   echo "$as_me:9278: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -9322,11 +9326,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:9325: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:9329: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:9329: \$? = $ac_status" >&5
+   echo "$as_me:9333: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -12125,7 +12129,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12128 "configure"
+#line 12132 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12221,7 +12225,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12224 "configure"
+#line 12228 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -14241,11 +14245,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:14244: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:14248: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:14248: \$? = $ac_status" >&5
+   echo "$as_me:14252: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -14340,11 +14344,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:14343: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:14347: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:14347: \$? = $ac_status" >&5
+   echo "$as_me:14351: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -14392,11 +14396,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:14395: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:14399: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:14399: \$? = $ac_status" >&5
+   echo "$as_me:14403: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -15451,13 +15455,32 @@ else
 fi
 
 
+# Check for single-precision compilation
+# Check whether --enable-float was given.
+if test "${enable_float+set}" = set; then
+  enableval=$enable_float; if test x$enableval = "xyes"; then
+        enable_float=$enableval
+    fi
+else
+  enable_float=no
+fi
+
+ if test "$enable_float" = yes; then
+  FLOAT_PRECISION_TRUE=
+  FLOAT_PRECISION_FALSE='#'
+else
+  FLOAT_PRECISION_TRUE='#'
+  FLOAT_PRECISION_FALSE=
+fi
+
+
+
 # Check for FFTW3
 # Check for header <fftw3.h> AC_CHECK_HEADERS doesnt work, since we must
 # use mpicc to get includes - cpp isnt always the same compiler.
-{ $as_echo "$as_me:$LINENO: checking for fftw3.h" >&5
+  { $as_echo "$as_me:$LINENO: checking for fftw3.h" >&5
 $as_echo_n "checking for fftw3.h... " >&6; }
-
-cat >conftest.$ac_ext <<_ACEOF
+  cat >conftest.$ac_ext <<_ACEOF
 /* confdefs.h.  */
 _ACEOF
 cat confdefs.h >>conftest.$ac_ext
@@ -15491,9 +15514,165 @@ $as_echo "$ac_try_echo") >&5
 	 test ! -s conftest.err
        } && test -s conftest.$ac_objext; then
 
-# ok, look for library files too
-{ $as_echo "$as_me:$LINENO: result: yes" >&5
+  # ok, look for library files too
+  { $as_echo "$as_me:$LINENO: result: yes" >&5
 $as_echo "yes" >&6; }
+   if test "$enable_float" = yes; then
+
+{ $as_echo "$as_me:$LINENO: checking for main in -lfftw3f" >&5
+$as_echo_n "checking for main in -lfftw3f... " >&6; }
+if test "${ac_cv_lib_fftw3f_main+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lfftw3f  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+return main ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+	 test "$cross_compiling" = yes ||
+	 $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_fftw3f_main=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_cv_lib_fftw3f_main=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_fftw3f_main" >&5
+$as_echo "$ac_cv_lib_fftw3f_main" >&6; }
+if test "x$ac_cv_lib_fftw3f_main" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBFFTW3F 1
+_ACEOF
+
+  LIBS="-lfftw3f $LIBS"
+
+else
+  { { $as_echo "$as_me:$LINENO: error: Cannot find fftw3f library" >&5
+$as_echo "$as_me: error: Cannot find fftw3f library" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+
+{ $as_echo "$as_me:$LINENO: checking for fftwf_plan_with_nthreads in -lfftw3f_threads" >&5
+$as_echo_n "checking for fftwf_plan_with_nthreads in -lfftw3f_threads... " >&6; }
+if test "${ac_cv_lib_fftw3f_threads_fftwf_plan_with_nthreads+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lfftw3f_threads  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char fftwf_plan_with_nthreads ();
+int
+main ()
+{
+return fftwf_plan_with_nthreads ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+	 test "$cross_compiling" = yes ||
+	 $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_fftw3f_threads_fftwf_plan_with_nthreads=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_cv_lib_fftw3f_threads_fftwf_plan_with_nthreads=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_fftw3f_threads_fftwf_plan_with_nthreads" >&5
+$as_echo "$ac_cv_lib_fftw3f_threads_fftwf_plan_with_nthreads" >&6; }
+if test "x$ac_cv_lib_fftw3f_threads_fftwf_plan_with_nthreads" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBFFTW3F_THREADS 1
+_ACEOF
+
+  LIBS="-lfftw3f_threads $LIBS"
+
+else
+  { { $as_echo "$as_me:$LINENO: error: Cannot find fftw3f_threads library" >&5
+$as_echo "$as_me: error: Cannot find fftw3f_threads library" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+     FFT_LIBS="-lfftw3f_threads -lfftw3f"
+     CPPFLAGS="$CPPFLAGS -DFLOAT_PRECISION"
+   else
 
 { $as_echo "$as_me:$LINENO: checking for main in -lfftw3" >&5
 $as_echo_n "checking for main in -lfftw3... " >&6; }
@@ -15646,15 +15825,16 @@ $as_echo "$as_me: error: Cannot find fftw3_threads library" >&2;}
    { (exit 1); exit 1; }; }
 fi
 
- FFT_LIBS="-lfftw3_threads -lfftw3"
+     FFT_LIBS="-lfftw3_threads -lfftw3"
+   fi
 
 else
   $as_echo "$as_me: failed program was:" >&5
 sed 's/^/| /' conftest.$ac_ext >&5
 
 
-# not ok, echo a warning
-{ { $as_echo "$as_me:$LINENO: error: Cannot find the FFTW3 library. Please install it (with threads enabled) from http://www.fftw.org." >&5
+  # not ok, echo a warning
+  { { $as_echo "$as_me:$LINENO: error: Cannot find the FFTW3 library. Please install it (with threads enabled) from http://www.fftw.org." >&5
 $as_echo "$as_me: error: Cannot find the FFTW3 library. Please install it (with threads enabled) from http://www.fftw.org." >&2;}
    { (exit 1); exit 1; }; }
 
@@ -15662,6 +15842,7 @@ fi
 
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 
+
 #Check for FLTK
 # as g++ seems needed and not gcc for compilation,
 #just check for presence of fltk-config program
@@ -15865,6 +16046,13 @@ $as_echo "$as_me: error: conditional \"HAVE_MPI\" was never defined.
 Usually this means the macro was only invoked conditionally." >&2;}
    { (exit 1); exit 1; }; }
 fi
+if test -z "${FLOAT_PRECISION_TRUE}" && test -z "${FLOAT_PRECISION_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"FLOAT_PRECISION\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"FLOAT_PRECISION\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
 if test -z "${HAVE_FLTK_TRUE}" && test -z "${HAVE_FLTK_FALSE}"; then
   { { $as_echo "$as_me:$LINENO: error: conditional \"HAVE_FLTK\" was never defined.
 Usually this means the macro was only invoked conditionally." >&5
@@ -16194,7 +16382,7 @@ exec 6>&1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by relion $as_me 1.3, which was
+This file was extended by relion $as_me 1.4, which was
 generated by GNU Autoconf 2.63.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -16257,7 +16445,7 @@ Report bugs to <bug-autoconf at gnu.org>."
 _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_version="\\
-relion config.status 1.3
+relion config.status 1.4
 configured by $0, generated by GNU Autoconf 2.63,
   with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
 
diff --git a/configure.ac b/configure.ac
index 57cbba9..c85ec7b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,14 +1,14 @@
 ## LibRelion
 
-AC_INIT([relion], [1.3], [scheres at mrc-lmb.cam.ac.uk], [relion], [http://www.relion.org])
+AC_INIT([relion], [1.4], [scheres at mrc-lmb.cam.ac.uk], [relion], [http://www.relion.org])
 AM_INIT_AUTOMAKE([-Wall no-define])
 
 # Define these substitions here to keep all version information in one place.
 # For information on how to properly maintain the library version information,
 # refer to the libtool manual, section "Updating library version information":
 # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
-AC_SUBST([RELION_SO_VERSION], [1:3:0])
-AC_SUBST([RELION_API_VERSION], [1.3])
+AC_SUBST([RELION_SO_VERSION], [1:4:0])
+AC_SUBST([RELION_API_VERSION], [1.4])
 
 # Generate two configuration headers; one for building the library itself with
 # an autogenerated template, and a second one that will be installed alongside
@@ -34,23 +34,41 @@ AC_ARG_ENABLE([mpi],
     [enable_mpi=no])
 AM_CONDITIONAL(HAVE_MPI, test "$enable_mpi" = yes)
 
+# Check for single-precision compilation
+AC_ARG_ENABLE([float],
+    AC_HELP_STRING([--enable-float],
+        [compile with float-precision]),
+    if test x$enableval = "xyes"; then
+        enable_float=$enableval
+    fi,
+    [enable_float=no])
+AM_CONDITIONAL(FLOAT_PRECISION, test "$enable_float" = yes)
+
+
 # Check for FFTW3
 # Check for header <fftw3.h> AC_CHECK_HEADERS doesnt work, since we must
 # use mpicc to get includes - cpp isnt always the same compiler.
-AC_MSG_CHECKING([for fftw3.h])
+  AC_MSG_CHECKING([for fftw3.h])
+  AC_COMPILE_IFELSE(
+  [AC_LANG_PROGRAM([[#include <fftw3.h>]],)],
+  [
+  # ok, look for library files too
+  AC_MSG_RESULT(yes)
+   if test "$enable_float" = yes; then
+     AC_CHECK_LIB([fftw3f],main,,AC_MSG_ERROR([Cannot find fftw3f library]))
+     AC_CHECK_LIB([fftw3f_threads],fftwf_plan_with_nthreads,,AC_MSG_ERROR([Cannot find fftw3f_threads library]))
+     FFT_LIBS="-lfftw3f_threads -lfftw3f"
+     CPPFLAGS="$CPPFLAGS -DFLOAT_PRECISION"
+   else
+     AC_CHECK_LIB([fftw3],main,,AC_MSG_ERROR([Cannot find fftw3 library]))
+     AC_CHECK_LIB([fftw3_threads],fftw_plan_with_nthreads,,AC_MSG_ERROR([Cannot find fftw3_threads library]))
+     FFT_LIBS="-lfftw3_threads -lfftw3"
+   fi
+  ],[
+  # not ok, echo a warning
+  AC_MSG_ERROR([Cannot find the FFTW3 library. Please install it (with threads enabled) from http://www.fftw.org.])
+  ])
 
-AC_COMPILE_IFELSE(
-[AC_LANG_PROGRAM([[#include <fftw3.h>]],)],
-[
-# ok, look for library files too
-AC_MSG_RESULT(yes)
- AC_CHECK_LIB([fftw3],main,,AC_MSG_ERROR([Cannot find fftw3 library]))
- AC_CHECK_LIB([fftw3_threads],fftw_plan_with_nthreads,,AC_MSG_ERROR([Cannot find fftw3_threads library]))
- FFT_LIBS="-lfftw3_threads -lfftw3"
-],[
-# not ok, echo a warning
-AC_MSG_ERROR([Cannot find the FFTW3 library. Please install it (with threads enabled) from http://www.fftw.org.])
-])
 
 #Check for FLTK
 # as g++ seems needed and not gcc for compilation, 
diff --git a/scripts/qsub.csh b/scripts/qsub.csh
index 0679207..727c430 100644
--- a/scripts/qsub.csh
+++ b/scripts/qsub.csh
@@ -1,8 +1,9 @@
 #!/bin/tcsh
-#$ -pe XXXqueueXXX XXXmpinodesXXX
-#$ -l dedicated=XXXthreadsXXX 
+#$ -pe XXXqueueXXX XXXnodesXXX
+#$ -l dedicated=XXXdedicatedXXX 
 #$ -e XXXerrfileXXX
 #$ -o XXXoutfileXXX
+#$ -A Relion 
 #$ -cwd
 #$ -S /bin/tcsh
 
diff --git a/src/apps/autopick_mpi.cpp b/src/apps/autopick_mpi.cpp
index 854581f..1394e69 100644
--- a/src/apps/autopick_mpi.cpp
+++ b/src/apps/autopick_mpi.cpp
@@ -35,9 +35,10 @@ int main(int argc, char *argv[])
 
     catch (RelionError XE)
     {
-        prm.usage();
+    	if (prm.verb > 0)
+    		prm.usage();
         std::cout << XE;
-        exit(1);
+        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
     }
 
     return 0;
diff --git a/src/apps/find_tiltpairs.cpp b/src/apps/find_tiltpairs.cpp
index 18744e6..363d259 100644
--- a/src/apps/find_tiltpairs.cpp
+++ b/src/apps/find_tiltpairs.cpp
@@ -31,17 +31,17 @@ public:
 
 	FileName fn_unt, fn_til, fn_out;
 	MetaDataTable MDunt, MDtil;
-	double tilt, tilt0, tiltF, tiltStep;
-	double rot, rot0, rotF, rotStep;
+	DOUBLE tilt, tilt0, tiltF, tiltStep;
+	DOUBLE rot, rot0, rotF, rotStep;
         int size, dim;
 	int x0, xF, xStep;
 	int y0, yF, yStep;
-	double acc;
+	DOUBLE acc;
 	int mind2;
 	bool do_opt;
-	double best_rot, best_tilt;
+	DOUBLE best_rot, best_tilt;
 	int best_x, best_y;
-	Matrix2D<double> Pass;
+	Matrix2D<DOUBLE> Pass;
 	std::vector<int> p_unt, p_til, p_map, pairs_t2u;
 	// I/O Parser
 	IOParser parser;
@@ -126,7 +126,7 @@ public:
 		if (!MDtil.containsLabel(EMDL_IMAGE_COORD_X) || !MDtil.containsLabel(EMDL_IMAGE_COORD_Y))
 			REPORT_ERROR("ERROR: Tilted STAR file does not contain the rlnCoordinateX or Y labels");
 
-		double x, y;
+		DOUBLE x, y;
 
 		p_unt.clear();
 		FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDunt)
@@ -182,7 +182,7 @@ public:
 
 	}
 
-	double getAverageDistance(int dx=0, int dy=0)
+	DOUBLE getAverageDistance(int dx=0, int dy=0)
 	{
 
 
@@ -191,7 +191,7 @@ public:
 		fn_map = "dist.txt";
 		fh.open(fn_map.c_str(), std::ios::out);
 
-		double result = 0.;
+		DOUBLE result = 0.;
 		int count = 0;
 		for (int t = 0; t < pairs_t2u.size(); t++)
 		{
@@ -208,7 +208,7 @@ public:
 				count ++;
 			}
 		}
-		result /= (double)count;
+		result /= (DOUBLE)count;
 		fh.close();
 		return result;
 
@@ -218,7 +218,7 @@ public:
 	{
 
 		int nprune = 0;
-		// Prune for double pairs
+		// Prune for DOUBLE pairs
 		for (int t = 0; t < pairs_t2u.size(); t++)
 		{
 			int u = pairs_t2u[t];
@@ -260,8 +260,8 @@ public:
 		p_map.resize(p_unt.size());
 		for (int u = 0; u < p_map.size()/2; u++)
 		{
-			double xu = (double)p_unt[2*u];
-			double yu = (double)p_unt[2*u+1];
+			DOUBLE xu = (DOUBLE)p_unt[2*u];
+			DOUBLE yu = (DOUBLE)p_unt[2*u+1];
 
 			p_map[2*u] = ROUND(MAT_ELEM(Pass, 0, 0) * xu + MAT_ELEM(Pass, 0, 1) * yu + MAT_ELEM(Pass, 0, 2));
 			p_map[2*u+1] = ROUND(MAT_ELEM(Pass, 1, 0) * xu + MAT_ELEM(Pass, 1, 1) * yu + MAT_ELEM(Pass, 1, 2));
@@ -270,10 +270,10 @@ public:
 	}
 
 
-	double optimiseTransformationMatrix(bool do_optimise_nr_pairs)
+	DOUBLE optimiseTransformationMatrix(bool do_optimise_nr_pairs)
 	{
 		std::vector<int> best_pairs_t2u, best_map;
-		double score, best_score, best_dist=9999.;
+		DOUBLE score, best_score, best_dist=9999.;
 		if (do_optimise_nr_pairs)
 			best_score = 0.;
 		else
@@ -285,12 +285,12 @@ public:
 		nn *= XMIPP_MAX(1., (yF-y0)/yStep);
 		int n = 0;
 		init_progress_bar(nn);
-		for (double rot = rot0; rot <= rotF; rot+= rotStep)
+		for (DOUBLE rot = rot0; rot <= rotF; rot+= rotStep)
 		{
-			for (double tilt = tilt0; tilt <= tiltF; tilt+= tiltStep)
+			for (DOUBLE tilt = tilt0; tilt <= tiltF; tilt+= tiltStep)
 			{
 				// Assume tilt-axis lies in-plane...
-				double psi = -rot;
+				DOUBLE psi = -rot;
 				// Rotate all points correspondingly
 				Euler_angles2matrix(rot, tilt, psi, Pass);
 				//std::cerr << " Pass= " << Pass << std::endl;
@@ -309,7 +309,7 @@ public:
                                                 bool is_best = false;
                                                 if (do_optimise_nr_pairs && score==best_score)
                                                 {
-                                                    double dist = getAverageDistance(x, y);
+                                                    DOUBLE dist = getAverageDistance(x, y);
                                                     if (dist < best_dist)
                                                     {
                                                         best_dist = dist;
@@ -347,7 +347,7 @@ public:
 	void optimiseTransformationMatrixContinuous()
 	{
 		// Get coordinates of all pairs:
-		Matrix2D<double> Au, Bt;
+		Matrix2D<DOUBLE> Au, Bt;
 	    Au.initZeros(3, 3);
 	    Bt.initZeros(3, 3);
 	    Pass.initZeros(4,4);
@@ -358,24 +358,24 @@ public:
 			int u = pairs_t2u[t];
 			if (u >= 0)
 	        {
-				Au(0, 0) += (double)(p_unt[2*u] * p_unt[2*u]);
-				Au(0, 1) += (double)(p_unt[2*u] * p_unt[2*u+1]);
-				Au(0, 2) += (double)(p_unt[2*u]);
+				Au(0, 0) += (DOUBLE)(p_unt[2*u] * p_unt[2*u]);
+				Au(0, 1) += (DOUBLE)(p_unt[2*u] * p_unt[2*u+1]);
+				Au(0, 2) += (DOUBLE)(p_unt[2*u]);
 				Au(1, 0) = Au(0, 1);
-				Au(1, 1) += (double)(p_unt[2*u+1] * p_unt[2*u+1]);
-				Au(1, 2) += (double)(p_unt[2*u+1]);
+				Au(1, 1) += (DOUBLE)(p_unt[2*u+1] * p_unt[2*u+1]);
+				Au(1, 2) += (DOUBLE)(p_unt[2*u+1]);
 				Au(2, 0) = Au(0, 2);
 				Au(2, 1) = Au(1, 2);
 				Au(2, 2) += 1.;
 
-				Bt(0, 0) += (double)(p_til[2*t] * p_unt[2*u]);
-				Bt(0, 1) += (double)(p_til[2*t+1] * p_unt[2*u]);
+				Bt(0, 0) += (DOUBLE)(p_til[2*t] * p_unt[2*u]);
+				Bt(0, 1) += (DOUBLE)(p_til[2*t+1] * p_unt[2*u]);
 				Bt(0, 2) = Au(0, 2);
-				Bt(1, 0) += (double)(p_til[2*t] * p_unt[2*u+1]);
-				Bt(1, 1) += (double)(p_til[2*t+1] * p_unt[2*u+1]);
+				Bt(1, 0) += (DOUBLE)(p_til[2*t] * p_unt[2*u+1]);
+				Bt(1, 1) += (DOUBLE)(p_til[2*t+1] * p_unt[2*u+1]);
 				Bt(1, 2) = Au(1, 2);
-				Bt(2, 0) += (double)(p_til[2*t]);
-				Bt(2, 1) += (double)(p_til[2*t+1]);
+				Bt(2, 0) += (DOUBLE)(p_til[2*t]);
+				Bt(2, 1) += (DOUBLE)(p_til[2*t+1]);
 				Bt(2,2) += 1.;
 	        }
 	    }
@@ -385,7 +385,7 @@ public:
 	    Pass = Pass.transpose();
 	    std::cout << " Optimised passing matrix= " << Pass << std::endl;
 	    //These values can be complete CRAP. Better not show them at all....
-	    //double rotp, tiltp, psip;
+	    //DOUBLE rotp, tiltp, psip;
 	    //tiltp = acos(Pass(1,1));
 	    //rotp = acos(Pass(1,0)/sin(tiltp));
 	    //psip = acos(Pass(0,1)/-sin(tiltp));
@@ -402,11 +402,11 @@ public:
 		// First do a crude search over the given parameter optimization space
 		// Optimize the number of pairs here...
 		int npart = optimiseTransformationMatrix(true);
-		// Get rid of double pairs (two different untilted coordinates are close to a tilted coordinate)
+		// Get rid of DOUBLE pairs (two different untilted coordinates are close to a tilted coordinate)
 		int nprune = 0;
 		nprune = prunePairs(best_x, best_y);
 		// Calculate average distance between the pairs
-		double avgdist = getAverageDistance(best_x, best_y);
+		DOUBLE avgdist = getAverageDistance(best_x, best_y);
 		std::cout << " Before optimization of the passing matrix: "<<std::endl;
 		std::cout << "  - Number of pruned pairs= "<<nprune<<std::endl;
 		std::cout << "  - best_rot= " << best_rot << " best_tilt= " << best_tilt << " best_x= " << best_x << " best_y= " << best_y << std::endl;
@@ -457,12 +457,12 @@ public:
 			if (u >= 0)
 			{
 				MDu.addObject();
-				MDu.setValue(EMDL_IMAGE_COORD_X, ((double)(p_unt[2*u])));
-				MDu.setValue(EMDL_IMAGE_COORD_Y, ((double)(p_unt[2*u+1])));
+				MDu.setValue(EMDL_IMAGE_COORD_X, ((DOUBLE)(p_unt[2*u])));
+				MDu.setValue(EMDL_IMAGE_COORD_Y, ((DOUBLE)(p_unt[2*u+1])));
 
 				MDt.addObject();
-				MDt.setValue(EMDL_IMAGE_COORD_X, ((double)(p_til[2*t])));
-				MDt.setValue(EMDL_IMAGE_COORD_Y, ((double)(p_til[2*t+1])));
+				MDt.setValue(EMDL_IMAGE_COORD_X, ((DOUBLE)(p_til[2*t])));
+				MDt.setValue(EMDL_IMAGE_COORD_Y, ((DOUBLE)(p_til[2*t+1])));
 			}
 		}
 		fn_unt = fn_unt.withoutExtension() + "_pairs.star";
diff --git a/src/apps/image_handler.cpp b/src/apps/image_handler.cpp
index 72d41e7..69b142f 100644
--- a/src/apps/image_handler.cpp
+++ b/src/apps/image_handler.cpp
@@ -28,14 +28,21 @@
 class image_handler_parameters
 {
 	public:
-   	FileName fn_in, fn_out, fn_sel, fn_img, fn_sym, fn_sub, fn_mult, fn_div, fn_add, fn_subtract, fn_mtf;
+   	FileName fn_in, fn_out, fn_sel, fn_img, fn_sym, fn_sub, fn_mult, fn_div, fn_add, fn_subtract, fn_fsc, fn_adjust_power;
 	int bin_avg, avg_first, avg_last, edge_x0, edge_xF, edge_y0, edge_yF, filter_edge_width, new_box;
-	bool do_add_edge, do_flipXY, do_flipmXY, do_shiftCOM;
-	double multiply_constant, divide_constant, add_constant, subtract_constant, threshold_above, threshold_below, angpix, new_angpix, lowpass, highpass, bfactor, shift_x, shift_y, shift_z;
-   	// I/O Parser
+	bool do_add_edge, do_flipXY, do_flipmXY, do_shiftCOM, do_stats;
+	DOUBLE multiply_constant, divide_constant, add_constant, subtract_constant, threshold_above, threshold_below, angpix, new_angpix, lowpass, highpass, bfactor, shift_x, shift_y, shift_z;
+   	int verb;
+	// I/O Parser
 	IOParser parser;
 
-	Image<double> Iout;
+	Image<DOUBLE> Iout;
+	Image<DOUBLE> Iop;
+	MetaDataTable MD;
+
+	// Image size
+	int xdim, ydim, zdim;
+	long int ndim;
 
 	void usage()
 	{
@@ -48,8 +55,8 @@ class image_handler_parameters
 		parser.setCommandLine(argc, argv);
 
 		int general_section = parser.addSection("General options");
-	    fn_in = parser.getOption("--i", "Input image (.mrc) or movie/stack (.mrcs)");
-	    fn_out = parser.getOption("--o", "Output file");
+	    fn_in = parser.getOption("--i", "Input STAR file, image (.mrc) or movie/stack (.mrcs)");
+	    fn_out = parser.getOption("--o", "Output name (overwrite input if empty; for STAR-input: insert this string before each image's extension)", "");
 
 	    int cst_section = parser.addSection("image-by-constant operations");
 	    multiply_constant = textToFloat(parser.getOption("--multiply_constant", "Multiply the image(s) pixel values by this constant", "1"));
@@ -64,9 +71,11 @@ class image_handler_parameters
 	    fn_div = parser.getOption("--divide", "Divide input image(s) by the pixel values in this image", "");
 	    fn_add = parser.getOption("--add", "Add the pixel values in this image to the input image(s) ", "");
 	    fn_subtract = parser.getOption("--subtract", "Subtract the pixel values in this image to the input image(s) ", "");
+	    fn_fsc = parser.getOption("--fsc", "Calculate FSC curve of the input image with this image ", "");
+	    fn_adjust_power = parser.getOption("--adjust_power", "Adjust the power spectrum of the input image to be the same as this image ", "");
 
 	    int four_section = parser.addSection("per-image operations");
-	    fn_mtf = parser.getOption("--correct_mtf", "STAR-file with MTF values to correct for (rlnResolutionInversePixel and rlnMtfValue)", "");
+	    do_stats = parser.checkOption("--stats", "Calculate per-image statistics?");
 	    bfactor = textToFloat(parser.getOption("--bfactor", "Apply a B-factor (in A^2)", "0."));
 	    lowpass = textToFloat(parser.getOption("--lowpass", "Low-pass filter frequency (in A)", "-1."));
 	    highpass = textToFloat(parser.getOption("--highpass", "High-pass filter frequency (in A)", "-1."));
@@ -100,339 +109,413 @@ class image_handler_parameters
     	if (parser.checkForErrors())
     		REPORT_ERROR("Errors encountered on the command line (see above), exiting...");
 
-	}
+    	verb = (do_stats || fn_fsc !="") ? 0 : 1;
 
-	void run()
-	{
+	}
 
-		Image<double> Ihead, Iin;
-		MultidimArray<double> Mtmp;
 
-		Ihead.read(fn_in, false);
-		int xdim, ydim, zdim;
-		long int ndim;
-		Ihead.getDimensions(xdim, ydim, zdim, ndim);
 
-		if (zdim > 1 && (do_add_edge || do_flipXY || do_flipmXY))
-			REPORT_ERROR("ERROR: you cannot perform 2D operations like --add_edge, --flipXY or --flipmXY on 3D maps. If you intended to operate on a movie, use .mrcs extensions for stacks!");
+	void perImageOperations(Image<DOUBLE> &Iin, FileName &my_fn_out)
+	{
 
-		if (zdim > 1 && (bin_avg > 0 || (avg_first >= 0 && avg_last >= 0)))
-			REPORT_ERROR("ERROR: you cannot perform movie-averaging operations on 3D maps. If you intended to operate on a movie, use .mrcs extensions for stacks!");
+		Image<DOUBLE> Iout;
+		Iout().resize(Iin());
 
-		int avgndim = 1;
-		if (bin_avg > 0)
+		if (do_add_edge)
 		{
-			avgndim = ndim / bin_avg;
+			// Treat X-boundaries
+			FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY2D(Iin())
+			{
+				if (j < edge_x0)
+					DIRECT_A2D_ELEM(Iin(), i, j) = DIRECT_A2D_ELEM(Iin(), i, edge_x0);
+				else if (j > edge_xF)
+					DIRECT_A2D_ELEM(Iin(), i, j) = DIRECT_A2D_ELEM(Iin(), i, edge_xF);
+			}
+			// Treat Y-boundaries
+			FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY2D(Iin())
+			{
+				if (i < edge_y0)
+					DIRECT_A2D_ELEM(Iin(), i, j) = DIRECT_A2D_ELEM(Iin(), edge_y0, j);
+				else if (i > edge_yF)
+					DIRECT_A2D_ELEM(Iin(), i, j) = DIRECT_A2D_ELEM(Iin(), edge_yF, j);
+			}
 		}
-		Image<double> Iavg(xdim, ydim, zdim, avgndim);
-		Image<double> Iout(xdim, ydim, zdim);
-		Image<double> Iop;
-		if (fn_mult != "")
-			Iop.read(fn_mult);
-		else if (fn_div != "")
-			Iop.read(fn_div);
-		else if (fn_add != "")
-			Iop.read(fn_add);
-		else if (fn_subtract != "")
-			Iop.read(fn_subtract);
-
-		if (fn_mult != "" || fn_div != "" || fn_add != "" || fn_subtract != "")
-			if (XSIZE(Iop()) != xdim || YSIZE(Iop()) != ydim || ZSIZE(Iop()) != zdim)
-				REPORT_ERROR("Error: operate-image is not of the correct size");
 
-		// Read each frame and do whatever is asked
-		time_config();
-   		init_progress_bar(ndim);
-		bool do_rewrite;
-		for (long int nn = 0; nn < ndim; nn++)
+		// Flipping: this needs to be done from Iin to Iout (i.e. can't be done on-line on Iout only!)
+		if (do_flipXY)
 		{
-			if (ndim > 1)
-				Iin.read(fn_in, true, nn);
-			else
-				Iin.read(fn_in);
-
-			do_rewrite = false;
-
-			// 2D options
-			if (do_add_edge)
+			// Flip X/Y
+			FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY2D(Iin())
 			{
-				do_rewrite = true;
-				// Treat X-boundaries
-				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY2D(Iin())
-				{
-					if (j < edge_x0)
-						DIRECT_A2D_ELEM(Iin(), i, j) = DIRECT_A2D_ELEM(Iin(), i, edge_x0);
-					else if (j > edge_xF)
-						DIRECT_A2D_ELEM(Iin(), i, j) = DIRECT_A2D_ELEM(Iin(), i, edge_xF);
-				}
-				// Treat Y-boundaries
-				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY2D(Iin())
-				{
-					if (i < edge_y0)
-						DIRECT_A2D_ELEM(Iin(), i, j) = DIRECT_A2D_ELEM(Iin(), edge_y0, j);
-					else if (i > edge_yF)
-						DIRECT_A2D_ELEM(Iin(), i, j) = DIRECT_A2D_ELEM(Iin(), edge_yF, j);
-				}
+				DIRECT_A2D_ELEM(Iout(), i, j) = DIRECT_A2D_ELEM(Iin(), j, i);
+
 			}
-			// Flipping: this needs to be done from Iin to Iout (i.e. can't be done on-line on Iout only!)
-			if (do_flipXY)
+		}
+		else if (do_flipmXY)
+		{
+			// Flip mX/Y
+			FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY2D(Iin())
 			{
-				do_rewrite = true;
-				// Flip X/Y
-				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY2D(Iin())
-				{
-					DIRECT_A2D_ELEM(Iout(), i, j) = DIRECT_A2D_ELEM(Iin(), j, i);
-
-				}
+				DIRECT_A2D_ELEM(Iout(), i, j) = DIRECT_A2D_ELEM(Iin(), XSIZE(Iin()) - 1 - j, YSIZE(Iin()) - 1 - i);
 			}
-			else if (do_flipmXY)
+		}
+		else
+		{
+			Iout = Iin;
+		}
+
+		// From here on also 3D options
+		if (fabs(multiply_constant - 1.) > 0.)
+		{
+			FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
 			{
-				do_rewrite = true;
-				// Flip mX/Y
-				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY2D(Iin())
-				{
-					DIRECT_A2D_ELEM(Iout(), i, j) = DIRECT_A2D_ELEM(Iin(), XSIZE(Iin()) - 1 - j, YSIZE(Iin()) - 1 - i);
-				}
+				DIRECT_A3D_ELEM(Iout(), k, i, j) *= multiply_constant;
 			}
-			else
+		}
+		else if (fabs(divide_constant - 1.) > 0.)
+		{
+			FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
 			{
-				Iout = Iin;
+				DIRECT_A3D_ELEM(Iout(), k, i, j) /= divide_constant;
 			}
-
-
-			// From here on also 3D options
-			if (fabs(multiply_constant - 1.) > 0.)
+		}
+		else if (fabs(add_constant) > 0.)
+		{
+			FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
 			{
-				do_rewrite = true;
-				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
-				{
-					DIRECT_A3D_ELEM(Iout(), k, i, j) *= multiply_constant;
-				}
+				DIRECT_A3D_ELEM(Iout(), k, i, j) += add_constant;
 			}
-			else if (fabs(divide_constant - 1.) > 0.)
+		}
+		else if (fabs(subtract_constant) > 0.)
+		{
+			FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
 			{
-				do_rewrite = true;
-				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
-				{
-					DIRECT_A3D_ELEM(Iout(), k, i, j) /= divide_constant;
-				}
+				DIRECT_A3D_ELEM(Iout(), k, i, j) -= subtract_constant;
 			}
-			else if (fabs(add_constant) > 0.)
+		}
+		else if (fn_mult != "")
+		{
+			FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
 			{
-				do_rewrite = true;
-				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
-				{
-					DIRECT_A3D_ELEM(Iout(), k, i, j) += add_constant;
-				}
+				DIRECT_A3D_ELEM(Iout(), k, i, j) *= DIRECT_A3D_ELEM(Iop(), k, i, j);
 			}
-			else if (fabs(subtract_constant) > 0.)
+		}
+		else if (fn_div != "")
+		{
+                    bool is_first = true;
+			FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
 			{
-				do_rewrite = true;
-				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
-				{
-					DIRECT_A3D_ELEM(Iout(), k, i, j) -= subtract_constant;
-				}
+                            if (ABS(DIRECT_A3D_ELEM(Iop(), k, i, j)) < 1e-10)
+                            {
+				if (is_first)
+                                {
+                                    std::cout << "Warning: ignore very small pixel values in divide image..." << std::endl;
+                                    is_first = false;
+                                }
+                                DIRECT_A3D_ELEM(Iout(), k, i, j) = 0.;
+                            }
+                            else
+				DIRECT_A3D_ELEM(Iout(), k, i, j) /= DIRECT_A3D_ELEM(Iop(), k, i, j);
 			}
-			else if (fn_mult != "")
+		}
+		else if (fn_add != "")
+		{
+			FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
 			{
-				do_rewrite = true;
-				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
-				{
-					DIRECT_A3D_ELEM(Iout(), k, i, j) *= DIRECT_A3D_ELEM(Iop(), k, i, j);
-				}
+				DIRECT_A3D_ELEM(Iout(), k, i, j) += DIRECT_A3D_ELEM(Iop(), k, i, j);
 			}
-			else if (fn_div != "")
+		}
+		else if (fn_subtract != "")
+		{
+			FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
 			{
-				do_rewrite = true;
-				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
-				{
-					if (DIRECT_A3D_ELEM(Iop(), k, i, j) < 1e-10)
-						std::cout << "Warning: very small pixel values in divide image..." << std::endl;
-					DIRECT_A3D_ELEM(Iout(), k, i, j) /= DIRECT_A3D_ELEM(Iop(), k, i, j);
-				}
+				DIRECT_A3D_ELEM(Iout(), k, i, j) -= DIRECT_A3D_ELEM(Iop(), k, i, j);
 			}
-			else if (fn_add != "")
+		}
+		else if (fn_fsc != "")
+		{
+			/// TODO
+			MultidimArray<DOUBLE> fsc;
+			MetaDataTable MDfsc;
+			getFSC(Iout(), Iop(), fsc);
+			MDfsc.setName("fsc");
+			FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY1D(fsc)
 			{
-				do_rewrite = true;
-				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
-				{
-					DIRECT_A3D_ELEM(Iout(), k, i, j) += DIRECT_A3D_ELEM(Iop(), k, i, j);
-				}
+				MDfsc.addObject();
+				DOUBLE res = (i > 0) ? (XSIZE(Iout()) * angpix / (DOUBLE)i) : 999.;
+				MDfsc.setValue(EMDL_SPECTRAL_IDX, (int)i);
+				MDfsc.setValue(EMDL_RESOLUTION, 1./res);
+				MDfsc.setValue(EMDL_RESOLUTION_ANGSTROM, res);
+				MDfsc.setValue(EMDL_POSTPROCESS_FSC_GENERAL, DIRECT_A1D_ELEM(fsc, i) );
 			}
-			else if (fn_subtract != "")
+			MDfsc.write(std::cout);
+		}
+		else if (fn_adjust_power != "")
+		{
+			MultidimArray<DOUBLE> spectrum;
+			getSpectrum(Iop(), spectrum, AMPLITUDE_SPECTRUM);
+			adaptSpectrum(Iin(), Iout(), spectrum, AMPLITUDE_SPECTRUM);
+		}
+
+		if (fabs(bfactor) > 0.)
+			applyBFactorToMap(Iout(), bfactor, angpix);
+
+		if (lowpass > 0.)
+			lowPassFilterMap(Iout(), lowpass, angpix, filter_edge_width);
+
+		if (highpass > 0.)
+			highPassFilterMap(Iout(), highpass, angpix, filter_edge_width);
+
+		// Shifting
+		if (do_shiftCOM)
+			selfTranslateCenterOfMassToCenter(Iout(), DONT_WRAP);
+		else if (fabs(shift_x) > 0. || fabs(shift_y) > 0. || fabs(shift_z) > 0.)
+		{
+			Matrix1D<DOUBLE> shift(2);
+			XX(shift) = shift_x;
+			YY(shift) = shift_y;
+			if (zdim > 1)
 			{
-				do_rewrite = true;
-				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
-				{
-					DIRECT_A3D_ELEM(Iout(), k, i, j) -= DIRECT_A3D_ELEM(Iop(), k, i, j);
-				}
+				shift.resize(3);
+				ZZ(shift) = shift_z;
 			}
+			selfTranslate(Iout(), shift, DONT_WRAP);
+		}
 
-			// More 2D/3D stuff
-			if (fn_mtf != "")
+		// Re-scale
+		if (new_angpix > 0.)
+		{
+			int oldsize = XSIZE(Iout());
+			int newsize = ROUND(oldsize * (angpix / new_angpix));
+			resizeMap(Iout(), newsize);
+			// Also reset the sampling rate in the header
+			Iout.MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_X, new_angpix);
+			Iout.MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Y, new_angpix);
+			if (Iout().getDim() == 3)
+				Iout.MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Z, new_angpix);
+		}
+		// Re-window
+		if (new_box > 0)
+		{
+			Iout().setXmippOrigin();
+			if (Iout().getDim() == 2)
 			{
-				do_rewrite = true;
-				correctMapForMTF(Iout(), fn_mtf);
+				Iout().window(FIRST_XMIPP_INDEX(new_box), FIRST_XMIPP_INDEX(new_box),
+						   LAST_XMIPP_INDEX(new_box),  LAST_XMIPP_INDEX(new_box));
 			}
-			if (fabs(bfactor) > 0.)
+			else if (Iout().getDim() == 3)
 			{
-				do_rewrite = true;
-				applyBFactorToMap(Iout(), bfactor, angpix);
+				Iout().window(FIRST_XMIPP_INDEX(new_box), FIRST_XMIPP_INDEX(new_box), FIRST_XMIPP_INDEX(new_box),
+						   LAST_XMIPP_INDEX(new_box),  LAST_XMIPP_INDEX(new_box),  LAST_XMIPP_INDEX(new_box));
 			}
-			if (lowpass > 0.)
+		}
+
+		if (fn_sym != "")
+			symmetriseMap(Iout(), fn_sym);
+
+		// Thresholding (can be done after any other operation)
+		if (fabs(threshold_above - 999.) > 0.)
+		{
+			FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
 			{
-				do_rewrite = true;
-				lowPassFilterMap(Iout(), lowpass, angpix, filter_edge_width);
+				if (DIRECT_A3D_ELEM(Iout(), k, i, j) > threshold_above)
+					DIRECT_A3D_ELEM(Iout(), k, i, j) = threshold_above;
 			}
-			if (highpass > 0.)
+		}
+		if (fabs(threshold_below + 999.) > 0.)
+		{
+			FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
 			{
-				do_rewrite = true;
-				highPassFilterMap(Iout(), lowpass, angpix, filter_edge_width);
+				if (DIRECT_A3D_ELEM(Iout(), k, i, j) < threshold_below)
+					DIRECT_A3D_ELEM(Iout(), k, i, j) = threshold_below;
 			}
+		}
 
-			// Shifting
-			if (do_shiftCOM)
+		// Write out the result
+		// Check whether fn_out has an "@": if so REPLACE the corresponding frame in the output stack!
+		long int n;
+		FileName fn_tmp;
+		my_fn_out.decompose(n, fn_tmp);
+		n--;
+		if (n >= 0) // This is a stack...
+		{
+
+			// If an output name was not specified: just replace the input image (in an existing stack)
+			if (fn_out == "")
 			{
-				do_rewrite = true;
-				selfTranslateCenterOfMassToCenter(Iout(), DONT_WRAP);
+				Iout.write(fn_tmp, n, true, WRITE_REPLACE); // replace an image in an existing stack
 			}
-			else if (fabs(shift_x) > 0. || fabs(shift_y) > 0. || fabs(shift_z) > 0.)
+			else
 			{
-				do_rewrite = true;
-				Matrix1D<double> shift(2);
-				XX(shift) = shift_x;
-				YY(shift) = shift_y;
-				if (zdim > 1)
-				{
-					shift.resize(3);
-					ZZ(shift) = shift_z;
-				}
-				selfTranslate(Iout(), shift, DONT_WRAP);
+				// The following assumes the images in the stack come ordered...
+				if (n == 0)
+					Iout.write(fn_tmp, n, true, WRITE_OVERWRITE); // make a new stack
+				else
+					Iout.write(fn_tmp, n, true, WRITE_APPEND);
 			}
+		}
+		else
+			Iout.write(my_fn_out);
 
-			// Re-scale
-			if (new_angpix > 0.)
-			{
-				do_rewrite = true;
+	}
 
-				int oldsize = XSIZE(Iout());
-				int newsize = ROUND(oldsize * (angpix / new_angpix));
-				resizeMap(Iout(), newsize);
+	void run()
+	{
 
-				// Also reset the sampling rate in the header
-				Iout.MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_X, new_angpix);
-				Iout.MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Y, new_angpix);
-				if (Iout().getDim() == 3)
-					Iout.MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Z, new_angpix);
+		// By default: write single output images
 
+		// Get a MetaDataTable
+		if (fn_in.getExtension() == "star")
+		{
+			MD.read(fn_in);
+		}
+		else if (fn_in.getExtension() == "mrcs" && !fn_in.contains("@"))
+		{
+			if (bin_avg > 0 || (avg_first >= 0 && avg_last >= 0))
+			{
+				MD.addObject();
+				MD.setValue(EMDL_IMAGE_NAME, fn_in);
 			}
-
-			// Re-window
-			if (new_box > 0)
+			else
 			{
-				do_rewrite = true;
-				Iout().setXmippOrigin();
-				if (Iout().getDim() == 2)
-				{
-					Iout().window(FIRST_XMIPP_INDEX(new_box), FIRST_XMIPP_INDEX(new_box),
-							   LAST_XMIPP_INDEX(new_box),  LAST_XMIPP_INDEX(new_box));
-				}
-				else if (Iout().getDim() == 3)
+				// Read the header to get the number of images inside the stack and generate that many lines in the MD
+				Image<DOUBLE> tmp;
+				FileName fn_tmp;
+				tmp.read(fn_in, false); //false means do not read image now, only header
+				for (int i = 1; i <= NSIZE(tmp()); i++)
 				{
-					Iout().window(FIRST_XMIPP_INDEX(new_box), FIRST_XMIPP_INDEX(new_box), FIRST_XMIPP_INDEX(new_box),
-							   LAST_XMIPP_INDEX(new_box),  LAST_XMIPP_INDEX(new_box),  LAST_XMIPP_INDEX(new_box));
+					MD.addObject();
+					fn_tmp.compose(i, fn_in);
+					MD.setValue(EMDL_IMAGE_NAME, fn_tmp);
 				}
 			}
+		}
+		else
+		{
+			// Just individual image input
+			MD.addObject();
+			MD.setValue(EMDL_IMAGE_NAME, fn_in);
+		}
+
+		int i_img = 0;
+		time_config();
+   		if (verb > 0)
+   			init_progress_bar(MD.numberOfObjects());
+
+		FOR_ALL_OBJECTS_IN_METADATA_TABLE(MD)
+		{
+			FileName fn_img;
+			MD.getValue(EMDL_IMAGE_NAME, fn_img);
 
-			// 3D-only stuff
-			if (fn_sym != "")
+			Image<DOUBLE> Iin;
+			// Initialise for the first image
+			if (i_img == 0)
 			{
-				do_rewrite = true;
-				symmetriseMap(Iout(), fn_sym);
+				Image<DOUBLE> Ihead;
+				Ihead.read(fn_img, false);
+				Ihead.getDimensions(xdim, ydim, zdim, ndim);
+
+				if (zdim > 1 && (do_add_edge || do_flipXY || do_flipmXY))
+					REPORT_ERROR("ERROR: you cannot perform 2D operations like --add_edge, --flipXY or --flipmXY on 3D maps. If you intended to operate on a movie, use .mrcs extensions for stacks!");
+
+				if (zdim > 1 && (bin_avg > 0 || (avg_first >= 0 && avg_last >= 0)))
+					REPORT_ERROR("ERROR: you cannot perform movie-averaging operations on 3D maps. If you intended to operate on a movie, use .mrcs extensions for stacks!");
+
+				if (fn_mult != "")
+					Iop.read(fn_mult);
+				else if (fn_div != "")
+					Iop.read(fn_div);
+				else if (fn_add != "")
+					Iop.read(fn_add);
+				else if (fn_subtract != "")
+					Iop.read(fn_subtract);
+				else if (fn_fsc != "")
+					Iop.read(fn_fsc);
+				else if (fn_adjust_power != "")
+					Iop.read(fn_adjust_power);
+
+				if (fn_mult != "" || fn_div != "" || fn_add != "" || fn_subtract != "" || fn_fsc != "" || fn_adjust_power != "")
+					if (XSIZE(Iop()) != xdim || YSIZE(Iop()) != ydim || ZSIZE(Iop()) != zdim)
+						REPORT_ERROR("Error: operate-image is not of the correct size");
+
 			}
 
-			// Thresholding (can be done after any other operation)
-			if (fabs(threshold_above - 999.) > 0.)
+
+			if (do_stats) // only write statistics to screen
 			{
-				do_rewrite = true;
-				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
-				{
-					if (DIRECT_A3D_ELEM(Iout(), k, i, j) > threshold_above)
-						DIRECT_A3D_ELEM(Iout(), k, i, j) = threshold_above;
-				}
+				Iin.read(fn_img);
+				DOUBLE avg, stddev, minval, maxval;
+				Iin().computeStats(avg, stddev, minval, maxval);
+				std::cout << fn_img << " : (x,y,z,n)= " << XSIZE(Iin()) << " x "<< YSIZE(Iin()) << " x "<< ZSIZE(Iin()) << " x "<< NSIZE(Iin()) << " ; avg= " << avg << " stddev= " << stddev << " minval= " <<minval << " maxval= " << maxval << std::endl;
 			}
-			if (fabs(threshold_below + 999.) > 0.)
+			else if (bin_avg > 0 || (avg_first >= 0 && avg_last >= 0))
 			{
-				do_rewrite = true;
-				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Iin())
+				// movie-frame averaging operations
+				int avgndim = 1;
+				if (bin_avg > 0)
 				{
-					if (DIRECT_A3D_ELEM(Iout(), k, i, j) < threshold_below)
-						DIRECT_A3D_ELEM(Iout(), k, i, j) = threshold_below;
+					avgndim = ndim / bin_avg;
 				}
-			}
+				Image<DOUBLE> Iavg(xdim, ydim, zdim, avgndim);
 
-			if (do_rewrite)
-			{
-				if (ndim > 1)
-				{
-					// Movies: first frame overwrite, then append
-					if (nn == 0)
-						Iout.write(fn_out, -1, (ndim > 1), WRITE_OVERWRITE);
-					else
-						Iout.write(fn_out, -1, false, WRITE_APPEND);
-				}
-				else
-				{
-					// Check whether fn_out has an "@": if so REPLACE the corresponding frame in the output stack!
-					long int n;
-					FileName fn_tmp;
-					fn_out.decompose(n,fn_tmp);
-					n--;
-					if (n >= 0)
-						Iout.write(fn_tmp, n, true, WRITE_REPLACE);
-					else
-						Iout.write(fn_out);
-				}
-			}
+				if (ndim == 1)
+					REPORT_ERROR("ERROR: you are trying to perform movie-averaging options on a single image/volume");
 
-			// Take care of averaging (this again is done in 2D!)
-			if (bin_avg > 0)
-			{
-				int myframe = nn / bin_avg;
-				if (myframe < avgndim)
+				FileName fn_ext = fn_out.getExtension();
+				if (NSIZE(Iavg()) > 1 && ( fn_ext.contains("mrc") && !fn_ext.contains("mrcs") ) )
+					REPORT_ERROR("ERROR: trying to write a stack into an MRC image. Use .mrcs extensions for stacks!");
+
+				for (long int nn = 0; nn < ndim; nn++)
 				{
-					FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY2D(Iout())
+					Iin.read(fn_img, true, nn);
+					if (bin_avg > 0)
 					{
-						DIRECT_NZYX_ELEM(Iavg(),myframe,0,i,j) += DIRECT_A2D_ELEM(Iout(), i, j); // just store sum
+						int myframe = nn / bin_avg;
+						if (myframe < avgndim)
+						{
+							FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY2D(Iin())
+							{
+								DIRECT_NZYX_ELEM(Iavg(),myframe,0,i,j) += DIRECT_A2D_ELEM(Iin(), i, j); // just store sum
+							}
+						}
+					}
+					else if (avg_first >= 0 && avg_last >= 0 && nn+1 >= avg_first && nn+1 <= avg_last) // add one to start counting at 1
+					{
+						FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Iin())
+						{
+							DIRECT_MULTIDIM_ELEM(Iavg(), n) += DIRECT_MULTIDIM_ELEM(Iin(), n); // just store sum
+						}
 					}
 				}
+				Iavg.write(fn_out);
 			}
-			else if (avg_first >= 0 && avg_last >= 0 && nn+1 >= avg_first && nn+1 <= avg_last) // add one to start counting at 1
+			else
 			{
-				FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Iout())
+				Iin.read(fn_img);
+				FileName my_fn_out;
+				if (fn_out == "")
+					my_fn_out = fn_img;
+				else
 				{
-					DIRECT_MULTIDIM_ELEM(Iavg(), n) += DIRECT_MULTIDIM_ELEM(Iout(), n); // just store sum
+					if (fn_in.getExtension() == "star" || (fn_in.getExtension() == "mrcs" && !fn_in.contains("@")))
+					{
+						my_fn_out = fn_img.insertBeforeExtension("_" + fn_out);
+					}
+					else
+					{
+						my_fn_out = fn_out;
+					}
 				}
+				perImageOperations(Iin, my_fn_out);
 			}
 
-			progress_bar(nn);
+			i_img++;
+			if (verb > 0)
+				progress_bar(i_img);
 		}
-   		progress_bar(ndim);
-
-   		if (bin_avg > 0 || (avg_first >= 0 && avg_last >= 0))
-   		{
-   			Iavg.write(fn_out);
-   			std::cout << " Done! Written output as: " << fn_out << std::endl;
-   		}
-   		else if (do_rewrite)
-   		{
-   			std::cout << " Done! Written output as: " << fn_out << std::endl;
-   		}
-   		else
-   		{
-   			std::cout << " Done nothing, please provide an operation to perform ... " << std::endl;
-   		}
 
+		if (verb > 0)
+			progress_bar(MD.numberOfObjects());
 
 	}
 
diff --git a/src/apps/maingui.cpp b/src/apps/maingui.cpp
index fcda86a..87d0b6c 100644
--- a/src/apps/maingui.cpp
+++ b/src/apps/maingui.cpp
@@ -24,6 +24,10 @@
 #include <unistd.h>
 #include <string.h>
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif 
+
 
 int main(int argc, char *argv[])
 {
@@ -54,8 +58,13 @@ int main(int argc, char *argv[])
 		short_dir[i] = '\0';
 	}
 
-	char titletext[57];
-	strcpy (titletext,"RELION: ");
+	char titletext[67];
+	strcpy (titletext,"RELION-");
+#ifdef PACKAGE_VERSION
+        strcat(titletext,PACKAGE_VERSION);
+#endif
+        strcat(titletext,": ");
+
 	strcat (titletext, short_dir);
 	RelionMainWindow window(GUIWIDTH, GUIHEIGHT, titletext);
 
diff --git a/src/apps/mask_create.cpp b/src/apps/mask_create.cpp
index e8ce4c6..a258f50 100644
--- a/src/apps/mask_create.cpp
+++ b/src/apps/mask_create.cpp
@@ -30,7 +30,7 @@ class mask_create_parameters
 {
 	public:
    	FileName fn_apply_in, fn_mask, fn_apply_out, fn_thr, fn_omask, fn_and, fn_or, fn_andnot, fn_ornot;
-   	double ini_threshold, extend_ini_mask, width_soft_edge;
+   	DOUBLE ini_threshold, extend_ini_mask, width_soft_edge;
 	bool do_invert;
    	IOParser parser;
 
@@ -68,7 +68,7 @@ class mask_create_parameters
 	void run()
 	{
 
-		Image<double> Iin, Iout, Ip;
+		Image<DOUBLE> Iin, Iout, Ip;
 		std:: cout << " Creating a mask ..." << std::endl;
 		Iin.read(fn_thr);
 
diff --git a/src/apps/particle_polish_mpi.cpp b/src/apps/particle_polish_mpi.cpp
index 16e32bc..d804846 100644
--- a/src/apps/particle_polish_mpi.cpp
+++ b/src/apps/particle_polish_mpi.cpp
@@ -35,9 +35,10 @@ int main(int argc, char *argv[])
 
     catch (RelionError XE)
     {
-        prm.usage();
+    	if (prm.verb > 0)
+    		prm.usage();
         std::cout << XE;
-        exit(1);
+        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
     }
 
     return 0;
diff --git a/src/apps/particle_sort_mpi.cpp b/src/apps/particle_sort_mpi.cpp
index 3eed873..621dccd 100644
--- a/src/apps/particle_sort_mpi.cpp
+++ b/src/apps/particle_sort_mpi.cpp
@@ -35,9 +35,10 @@ int main(int argc, char *argv[])
 
     catch (RelionError XE)
     {
-        prm.usage();
+    	if (prm.verb > 0)
+    		prm.usage();
         std::cout << XE;
-        exit(1);
+        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
     }
 
     return 0;
diff --git a/src/apps/preprocess_mpi.cpp b/src/apps/preprocess_mpi.cpp
index c393392..21d1f1d 100644
--- a/src/apps/preprocess_mpi.cpp
+++ b/src/apps/preprocess_mpi.cpp
@@ -37,7 +37,7 @@ int main(int argc, char *argv[])
     {
         prm.usage();
         std::cout << XE;
-        exit(1);
+        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
     }
 
     return 0;
diff --git a/src/apps/project.cpp b/src/apps/project.cpp
index 2dee6a9..923131a 100644
--- a/src/apps/project.cpp
+++ b/src/apps/project.cpp
@@ -37,10 +37,10 @@ class project_parameters
 public:
 
 	FileName fn_map, fn_ang, fn_out, fn_img, fn_model, fn_sym;
-	double rot, tilt, psi, xoff, yoff, zoff, angpix, maxres, stddev_white_noise, particle_diameter, ana_prob_range, ana_prob_step;
+	DOUBLE rot, tilt, psi, xoff, yoff, zoff, angpix, maxres, stddev_white_noise, particle_diameter, ana_prob_range, ana_prob_step;
 	int padding_factor;
 	int r_max, r_min_nn, interpolator;
-    bool do_only_one, do_ctf, ctf_phase_flipped, do_timing, do_add_noise, do_subtract_exp, do_ignore_particle_name;
+    bool do_only_one, do_ctf, do_ctf2, ctf_phase_flipped, do_ctf_intact_1st_peak, do_timing, do_add_noise, do_subtract_exp, do_ignore_particle_name, do_3d_rot;
 	// I/O Parser
 	IOParser parser;
 	MlModel model;
@@ -58,8 +58,9 @@ public:
 		int general_section = parser.addSection("Options");
 		fn_map = parser.getOption("--i", "Input map to be projected");
 		fn_out = parser.getOption("--o", "Rootname for output projections", "proj");
-       	do_ctf = parser.checkOption("--ctf", "Apply CTF to reference projections");
+       	do_ctf = parser.checkOption("--ctf", "apply ctf to reference projections");
        	ctf_phase_flipped = parser.checkOption("--ctf_phase_flip", "Flip phases of the CTF in the output projections");
+       	do_ctf_intact_1st_peak = parser.checkOption("--ctf_intact_first_peak", "Ignore CTFs until their first peak?");
        	angpix = textToFloat(parser.getOption("--angpix", "Pixel size (in Angstroms)", "1"));
        	fn_ang = parser.getOption("--ang", "STAR file with orientations for multiple projections (if None, assume single projection)","None");
        	rot = textToFloat(parser.getOption("--rot", "First Euler angle (for a single projection)", "0"));
@@ -74,10 +75,11 @@ public:
        	do_subtract_exp = parser.checkOption("--subtract_exp", "Subtract projections from experimental images (in --ang)");
        	do_ignore_particle_name = parser.checkOption("--ignore_particle_name", "Ignore the rlnParticleName column (in --ang)");
        	do_only_one = (fn_ang == "None");
+       	do_3d_rot = parser.checkOption("--3d_rot", "Perform 3D rotations instead of projection into 2D images");
 
        	maxres = textToFloat(parser.getOption("--maxres", "Maximum resolution (in Angstrom) to consider in Fourier space (default Nyquist)", "-1"));
        	padding_factor = textToInteger(parser.getOption("--pad", "Padding factor", "2"));
-
+    	do_ctf2 = parser.checkOption("--ctf2", "Apply CTF*CTF to reference projections");
        	if (parser.checkOption("--NN", "Use nearest-neighbour instead of linear interpolation"))
        		interpolator = NEAREST_NEIGHBOUR;
        	else
@@ -95,13 +97,13 @@ public:
 	void project()
 	{
 
-    	MetaDataTable DFo, MDang;
-    	Matrix2D<double> A3D;
+            MetaDataTable DFo, MDang;
+    	Matrix2D<DOUBLE> A3D;
     	FileName fn_expimg;
 
-    	MultidimArray<Complex > F2D, Fexpimg;
-    	MultidimArray<double> Fctf, dummy;
-    	Image<double> vol, img, expimg;
+    	MultidimArray<Complex > F3D, F2D, Fexpimg;
+    	MultidimArray<DOUBLE> Fctf, dummy;
+    	Image<DOUBLE> vol, img, expimg;
     	FourierTransformer transformer, transformer_expimg;
 
 		std::cerr << " Reading map: " << fn_map << std::endl;
@@ -111,7 +113,7 @@ public:
     	if (!do_only_one)
     	{
     		std::cerr << " Reading STAR file with all angles " << fn_ang << std::endl;
-    		MDang.read(fn_ang);
+                MDang.read(fn_ang);
     		std::cerr << " Done reading STAR file!" << std::endl;
     	}
 
@@ -122,26 +124,37 @@ public:
    			r_max = CEIL(XSIZE(vol()) * angpix / maxres);
 
     	// Set right size of F2D and initialize to zero
-   		img().resize(YSIZE(vol()), XSIZE(vol()));
+    	if (do_3d_rot)
+    		img().resize(ZSIZE(vol()), YSIZE(vol()), XSIZE(vol()));
+    	else
+    		img().resize(YSIZE(vol()), XSIZE(vol()));
     	transformer.setReal(img());
     	transformer.getFourierAlias(F2D);
 
     	// Set up the projector
-    	Projector projector((int)XSIZE(vol()), interpolator, padding_factor, r_min_nn);
+    	int data_dim = (do_3d_rot) ? 3 : 2;
+    	Projector projector((int)XSIZE(vol()), interpolator, padding_factor, r_min_nn, data_dim);
     	projector.computeFourierTransformMap(vol(), dummy, 2* r_max);
 
     	if (do_only_one)
     	{
-            Euler_rotation3DMatrix(rot, tilt, psi, A3D);
-            F2D.initZeros();
-            projector.get2DFourierTransform(F2D, A3D, IS_NOT_INV);
-
+    		Euler_rotation3DMatrix(rot, tilt, psi, A3D);
+    		F2D.initZeros();
+    		projector.get2DFourierTransform(F2D, A3D, IS_NOT_INV);
             if (ABS(xoff) > 0.001 || ABS(yoff) > 0.001)
             {
-            	Matrix1D<double> shift(2);
+            	Matrix1D<DOUBLE> shift(2);
             	XX(shift) = -xoff;
             	YY(shift) = -yoff;
-            	shiftImageInFourierTransform(F2D, F2D, XSIZE(vol()), shift);
+            	if (do_3d_rot)
+            	{
+            		shift.resize(3);
+            		ZZ(shift) = -zoff;
+            		shiftImageInFourierTransform(F2D, F2D, XSIZE(vol()), XX(shift), YY(shift), ZZ(shift));
+            	}
+            	else
+            		shiftImageInFourierTransform(F2D, F2D, XSIZE(vol()), XX(shift), YY(shift));
+
             }
         	transformer.inverseFourierTransform();
         	// Shift the image back to the center...
@@ -170,34 +183,47 @@ public:
             long int imgno = 0;
             FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDang)
             {
-            	MDang.getValue(EMDL_ORIENT_ROT, rot);
-            	MDang.getValue(EMDL_ORIENT_TILT, tilt);
-            	MDang.getValue(EMDL_ORIENT_PSI, psi);
-            	MDang.getValue(EMDL_ORIENT_ORIGIN_X, xoff);
-            	MDang.getValue(EMDL_ORIENT_ORIGIN_Y, yoff);
 
-            	Euler_rotation3DMatrix(rot, tilt, psi, A3D);
-            	F2D.initZeros();
-            	projector.get2DFourierTransform(F2D, A3D, IS_NOT_INV);
+                MDang.getValue(EMDL_ORIENT_ROT, rot);
+                MDang.getValue(EMDL_ORIENT_TILT, tilt);
+                MDang.getValue(EMDL_ORIENT_PSI, psi);
+                MDang.getValue(EMDL_ORIENT_ORIGIN_X, xoff);
+                MDang.getValue(EMDL_ORIENT_ORIGIN_Y, yoff);
+                if (do_3d_rot)
+                    MDang.getValue(EMDL_ORIENT_ORIGIN_Z, zoff);
 
-            	if (ABS(xoff) > 0.001 || ABS(yoff) > 0.001)
-            	{
-            		Matrix1D<double> shift(2);
-            		XX(shift) = -xoff;
-            		YY(shift) = -yoff;
-            		shiftImageInFourierTransform(F2D, F2D, XSIZE(vol()), shift );
-				}
-
-            	// Apply CTF if necessary
-            	CTF ctf;
-            	if (do_ctf)
-            	{
+                Euler_rotation3DMatrix(rot, tilt, psi, A3D);
+                F2D.initZeros();
+                projector.get2DFourierTransform(F2D, A3D, IS_NOT_INV);
+
+                if (ABS(xoff) > 0.001 || ABS(yoff) > 0.001)
+                {
+                    Matrix1D<DOUBLE> shift(2);
+                    XX(shift) = -xoff;
+                    YY(shift) = -yoff;
+
+                    if (do_3d_rot)
+                    {
+                        shift.resize(3);
+                        ZZ(shift) = -zoff;
+                        shiftImageInFourierTransform(F2D, F2D, XSIZE(vol()), XX(shift), YY(shift), ZZ(shift) );
+                    }
+                    else
+                    	shiftImageInFourierTransform(F2D, F2D, XSIZE(vol()), XX(shift), YY(shift) );
+                }
+
+                // Apply CTF if necessary
+                CTF ctf;
+                if (do_ctf || do_ctf2)
+                {
                     ctf.read(MDang, MDang);
                     Fctf.resize(F2D);
-                    ctf.getFftwImage(Fctf, XSIZE(vol()), XSIZE(vol()), angpix, ctf_phase_flipped, false, false, true);
+                    ctf.getFftwImage(Fctf, XSIZE(vol()), XSIZE(vol()), angpix, ctf_phase_flipped, false,  do_ctf_intact_1st_peak, true);
                     FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(F2D)
                     {
                         DIRECT_MULTIDIM_ELEM(F2D, n) *= DIRECT_MULTIDIM_ELEM(Fctf, n);
+                        if (do_ctf2)
+                        	DIRECT_MULTIDIM_ELEM(F2D, n) *= DIRECT_MULTIDIM_ELEM(Fctf, n);
                     }
                 }
 
@@ -236,7 +262,7 @@ public:
                         if (my_mic_id < 0)
                             REPORT_ERROR("ERROR: cannot find " + fn_group + " in the input model file...");
 
-                        double normcorr = 1.;
+                        DOUBLE normcorr = 1.;
                         if (MDang.containsLabel(EMDL_IMAGE_NORM_CORRECTION))
                         {
                             MDang.getValue(EMDL_IMAGE_NORM_CORRECTION, normcorr);
@@ -245,10 +271,10 @@ public:
                         // Add coloured noise
                         FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(F2D)
                         {
-                            int ires = ROUND( sqrt( (double)(kp*kp + ip*ip + jp*jp) ) );
+                            int ires = ROUND( sqrt( (DOUBLE)(kp*kp + ip*ip + jp*jp) ) );
                             ires = XMIPP_MIN(ires, model.ori_size/2); // at freqs higher than Nyquist: use last sigma2 value
 
-                            double sigma = sqrt(DIRECT_A1D_ELEM(model.sigma2_noise[my_mic_id], ires));
+                            DOUBLE sigma = sqrt(DIRECT_A1D_ELEM(model.sigma2_noise[my_mic_id], ires));
                             DIRECT_A3D_ELEM(F2D, k, i, j).real += rnd_gaus(0., sigma);
                             DIRECT_A3D_ELEM(F2D, k, i, j).imag += rnd_gaus(0., sigma);
                         }
@@ -272,17 +298,26 @@ public:
                 if (do_subtract_exp)
                 {
                     MDang.getValue(EMDL_IMAGE_NAME, fn_expimg);
+                    MDang.setValue(EMDL_IMAGE_ORI_NAME, fn_expimg); // Store fn_expimg in rlnOriginalParticleName
                     expimg.read(fn_expimg);
                     img() = expimg() - img();
                 }
 
-                // Write this particle to the stack on disc
-                // First particle: write stack in overwrite mode, from then on just append to it
-                fn_img.compose(imgno+1,fn_out+".mrcs");
-                if (imgno == 0)
-                    img.write(fn_img, -1, false, WRITE_OVERWRITE);
+                if (do_3d_rot)
+                {
+                    fn_img.compose(fn_out, imgno+1,"mrc");
+                    img.write(fn_img);
+                }
                 else
-                    img.write(fn_img, -1, false, WRITE_APPEND);
+                {
+                    // Write this particle to the stack on disc
+                    // First particle: write stack in overwrite mode, from then on just append to it
+                    fn_img.compose(imgno+1,fn_out+".mrcs");
+                    if (imgno == 0)
+                        img.write(fn_img, -1, false, WRITE_OVERWRITE);
+                    else
+                        img.write(fn_img, -1, false, WRITE_APPEND);
+                }
 
                 // Set the image name to the output STAR file
                 DFo.addObject();
diff --git a/src/apps/reconstruct.cpp b/src/apps/reconstruct.cpp
index 9d6c79b..87e84f4 100644
--- a/src/apps/reconstruct.cpp
+++ b/src/apps/reconstruct.cpp
@@ -32,8 +32,8 @@ class reconstruct_parameters
    	FileName fn_out, fn_sel, fn_img, fn_sym, fn_sub, fn_fsc, fn_debug;
 	MetaDataTable DF;
 	int r_max, r_min_nn, blob_order, padding_factor, ref_dim, interpolator, iter, nr_threads, debug_ori_size, debug_size, ctf_dim;
-	double blob_radius, blob_alpha, angular_error, shift_error, angpix, maxres, beamtilt_x, beamtilt_y;
-	bool do_ctf, ctf_phase_flipped, only_flip_phases, intact_ctf_first_peak, do_fom_weighting, do_beamtilt;
+	DOUBLE blob_radius, blob_alpha, angular_error, shift_error, angpix, maxres, beamtilt_x, beamtilt_y;
+	bool do_ctf, ctf_phase_flipped, only_flip_phases, intact_ctf_first_peak, do_fom_weighting, do_3d_rot, do_reconstruct_ctf, do_beamtilt;
 	// I/O Parser
 	IOParser parser;
 
@@ -85,6 +85,11 @@ class reconstruct_parameters
     	shift_error = textToFloat(parser.getOption("--shift_error", "Apply random deviations with this standard deviation (in pixels) to each of the 2 translations", "0."));
     	do_fom_weighting = parser.checkOption("--fom_weighting", "Weight particles according to their figure-of-merit (_rlnParticleFigureOfMerit)");
     	fn_fsc = parser.getOption("--fsc", "FSC-curve for regularized reconstruction", "");
+    	do_3d_rot = parser.checkOption("--3d_rot", "Perform 3D rotations instead of backprojections from 2D images");
+    	ctf_dim  = textToInteger(parser.getOption("--reconstruct_ctf", "Perform a 3D reconstruction from 2D CTF-images, with the given size in pixels", "-1"));
+    	do_reconstruct_ctf = (ctf_dim > 0);
+    	if (do_reconstruct_ctf)
+    		do_ctf = false;
 
     	// Hidden
        	r_min_nn = textToInteger(getParameter(argc, argv, "--r_min_nn", "10"));
@@ -107,13 +112,14 @@ class reconstruct_parameters
 	void reconstruct()
 	{
 
+		int data_dim = (do_3d_rot) ? 3 : 2;
 		if (fn_debug != "")
 		{
-			BackProjector backprojector(debug_ori_size, 3, fn_sym, interpolator, padding_factor, r_min_nn, blob_order, blob_radius, blob_alpha);
+			BackProjector backprojector(debug_ori_size, 3, fn_sym, interpolator, padding_factor, r_min_nn, blob_order, blob_radius, blob_alpha, data_dim);
 			backprojector.initialiseDataAndWeight(debug_size);
 			backprojector.data.printShape();
 			backprojector.weight.printShape();
-			Image<double> It;
+			Image<DOUBLE> It;
 			It.read(fn_debug+"_data_real.mrc");
 			It().setXmippOrigin();
 			It().xinit=0;
@@ -138,7 +144,7 @@ class reconstruct_parameters
 				A3D_ELEM(backprojector.weight, k, i, j) = A3D_ELEM(It(), k, i, j);
 			}
 
-			MultidimArray<double> dummy;
+			MultidimArray<DOUBLE> dummy;
 			backprojector.reconstruct(It(), iter, false, 1., dummy, dummy, dummy, dummy, 1., false, true, nr_threads, -1);
 	    	It.write(fn_out);
 	    	std::cerr<<" Done writing map in "<<fn_out<<std::endl;
@@ -148,13 +154,13 @@ class reconstruct_parameters
 		else
 		{
 
-		double rot, tilt, psi, fom;
-		Matrix2D<double> A3D;
+		DOUBLE rot, tilt, psi, fom;
+		Matrix2D<DOUBLE> A3D;
 		MultidimArray<Complex > Faux, F2D, Fsub;
-		MultidimArray<double> Fweight, Fctf, dummy;
-		Image<double> vol, img, sub;
+		MultidimArray<DOUBLE> Fweight, Fctf, dummy;
+		Image<DOUBLE> vol, img, sub;
 		FourierTransformer transformer;
-		Matrix1D< double > trans(2);
+		Matrix1D< DOUBLE > trans(2);
 		Projector proj;
 		int mysize;
 //#define DEBUG_WW
@@ -168,7 +174,7 @@ class reconstruct_parameters
 		BackProjector backprojectort(mysize, ref_dim, fn_sym, interpolator, padding_factor, r_min_nn, blob_order, blob_radius, blob_alpha);
 		backprojectort.initZeros(2 * r_max);
 
-		Image<double> Imagn, Iphas, Iw, tvol;
+		Image<DOUBLE> Imagn, Iphas, Iw, tvol;
 		Imagn.read("FEW_it24_rank2_data_magn.spi");
 		Iphas.read("FEW_it24_rank2_data_phas.spi");
 		Iw.read("FEW_it24_rank2_weight.spi");
@@ -176,7 +182,7 @@ class reconstruct_parameters
         Iw().xinit=0;
 
 		// Write out invw
-		Image<double> oo;
+		Image<DOUBLE> oo;
 		oo=Iw;
 		FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Iw())
 		{
@@ -190,8 +196,8 @@ class reconstruct_parameters
 		backprojectort.weight.printShape();
 		FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Imagn())
 		{
-			double realval = sin(DIRECT_MULTIDIM_ELEM(Iphas(), n)) * DIRECT_MULTIDIM_ELEM(Imagn(), n);
-			double imagval = cos(DIRECT_MULTIDIM_ELEM(Iphas(), n)) * DIRECT_MULTIDIM_ELEM(Imagn(), n);
+			DOUBLE realval = sin(DIRECT_MULTIDIM_ELEM(Iphas(), n)) * DIRECT_MULTIDIM_ELEM(Imagn(), n);
+			DOUBLE imagval = cos(DIRECT_MULTIDIM_ELEM(Iphas(), n)) * DIRECT_MULTIDIM_ELEM(Imagn(), n);
 			DIRECT_MULTIDIM_ELEM(backprojectort.data, n) = (Complex)(realval, imagval);
 		}
 		backprojectort.weight = Iw();
@@ -204,21 +210,31 @@ class reconstruct_parameters
 
 
    		// Get dimension of the images
-		(DF).firstObject();
-		DF.getValue(EMDL_IMAGE_NAME, fn_img);
-		img.read(fn_img);
-		mysize=(int)XSIZE(img());
+   		if (do_reconstruct_ctf)
+   		{
+   			mysize = ctf_dim;
+   			img().resize(ctf_dim, ctf_dim);
+   			img().setXmippOrigin();
+   		}
+   		else
+   		{
+			(DF).firstObject();
+			DF.getValue(EMDL_IMAGE_NAME, fn_img);
+			img.read(fn_img);
+			mysize=(int)XSIZE(img());
+   		}
+
 
    		if (DF.containsLabel(EMDL_CTF_MAGNIFICATION) && DF.containsLabel(EMDL_CTF_DETECTOR_PIXEL_SIZE))
     	{
-    		double mag, dstep;
+    		DOUBLE mag, dstep;
    			DF.getValue(EMDL_CTF_MAGNIFICATION, mag);
    			DF.getValue(EMDL_CTF_DETECTOR_PIXEL_SIZE, dstep);
    			angpix = 10000. * dstep / mag;
    			std::cout << " + Using pixel size calculated from magnification and detector pixel size in the input STAR file: " << angpix << std::endl;
     	}
 
-   		BackProjector backprojector(mysize, ref_dim, fn_sym, interpolator, padding_factor, r_min_nn, blob_order, blob_radius, blob_alpha);
+   		BackProjector backprojector(mysize, ref_dim, fn_sym, interpolator, padding_factor, r_min_nn, blob_order, blob_radius, blob_alpha, data_dim);
 
    		if (maxres < 0.)
    			r_max = -1;
@@ -245,9 +261,12 @@ class reconstruct_parameters
    		FOR_ALL_OBJECTS_IN_METADATA_TABLE(DF)
    		{
 
-   			DF.getValue(EMDL_IMAGE_NAME, fn_img);
-   			img.read(fn_img);
-   			img().setXmippOrigin();
+   			if (!do_reconstruct_ctf)
+   			{
+   				DF.getValue(EMDL_IMAGE_NAME, fn_img);
+   				img.read(fn_img);
+   				img().setXmippOrigin();
+   			}
 
 			// Rotations
 			if (ref_dim==2)
@@ -280,6 +299,13 @@ class reconstruct_parameters
    				XX(trans) += rnd_gaus(0., shift_error);
    				YY(trans) += rnd_gaus(0., shift_error);
    			}
+  			if (do_3d_rot)
+   			{
+   				trans.resize(3);
+   				DF.getValue( EMDL_ORIENT_ORIGIN_Z, ZZ(trans));
+   	  			if (shift_error > 0.)
+   	   				ZZ(trans) += rnd_gaus(0., shift_error);
+   			}
 
    			if (do_fom_weighting)
    				DF.getValue( EMDL_PARTICLE_FOM, fom);
@@ -290,12 +316,17 @@ class reconstruct_parameters
    			CenterFFT(img(), true);
    			transformer.FourierTransform(img(), F2D);
    			if (ABS(XX(trans)) > 0. || ABS(YY(trans)) > 0.)
-   				shiftImageInFourierTransform(F2D, F2D, XSIZE(img()), trans );
+   			{
+   				if (do_3d_rot)
+   					shiftImageInFourierTransform(F2D, F2D, XSIZE(img()), XX(trans), YY(trans), ZZ(trans));
+   				else
+   					shiftImageInFourierTransform(F2D, F2D, XSIZE(img()), XX(trans), YY(trans));
+   			}
 
 			Fctf.resize(F2D);
 			Fctf.initConstant(1.);
 			// Apply CTF if necessary
-			if (do_ctf)
+			if (do_ctf || do_reconstruct_ctf)
 			{
 				CTF ctf;
 				ctf.read(DF, DF);
@@ -336,7 +367,15 @@ class reconstruct_parameters
 			else
 			{
 				// "Normal" reconstruction, multiply X by CTF, and W by CTF^2
-				if (do_ctf)
+				if (do_reconstruct_ctf)
+				{
+					FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(F2D)
+					{
+						DIRECT_MULTIDIM_ELEM(F2D, n)  = DIRECT_MULTIDIM_ELEM(Fctf, n);
+						DIRECT_MULTIDIM_ELEM(Fctf, n) = 1.;
+					}
+				}
+				else if (do_ctf)
 				{
 					FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(F2D)
 					{
@@ -369,7 +408,7 @@ class reconstruct_parameters
 						//std::cerr << " exp_R_mic= " << exp_R_mic << std::endl;
 						std::cerr << " rot= " << rot << " tilt= " << tilt << " psi= " << psi << " xoff= "<< XX(trans)<< " yoff= "<<YY(trans)<<std::endl;
 						//std::cerr << "mic_id= "<<mic_id<<" mymodel.sigma2_noise[mic_id]= " << mymodel.sigma2_noise[mic_id] << std::endl;
-						Image<double> It;
+						Image<DOUBLE> It;
 						It()=Fctf;
 						It.write("reconstruct_Fctf.spi");
 						It().resize(mysize, mysize);
@@ -395,7 +434,7 @@ class reconstruct_parameters
 
    		bool do_map = false;
    		bool do_use_fsc = false;
-   		MultidimArray<double> fsc;
+   		MultidimArray<DOUBLE> fsc;
    		fsc.resize(mysize/2+1);
    		if (fn_fsc != "")
    		{
@@ -406,7 +445,7 @@ class reconstruct_parameters
    			FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDfsc)
    			{
    				int idx;
-   				double val;
+   				DOUBLE val;
    				MDfsc.getValue(EMDL_SPECTRAL_IDX, idx);
    				MDfsc.getValue(EMDL_MLMODEL_FSC_HALVES_REF, val);
    				fsc(idx) =  val;
@@ -415,6 +454,31 @@ class reconstruct_parameters
    		std::cerr << "Starting the reconstruction ..." << std::endl;
    		backprojector.reconstruct(vol(), iter, do_map, 1., dummy, dummy, dummy, fsc, 1., do_use_fsc, true, nr_threads, -1);
 
+   		if (do_reconstruct_ctf)
+   		{
+
+   			F2D.clear();
+   			transformer.FourierTransform(vol(), F2D);
+
+   			// CenterOriginFFT: Set the center of the FFT in the FFTW origin
+			Matrix1D<DOUBLE> shift(3);
+			XX(shift)=-(DOUBLE)(int)(ctf_dim / 2);
+			YY(shift)=-(DOUBLE)(int)(ctf_dim / 2);
+			ZZ(shift)=-(DOUBLE)(int)(ctf_dim / 2);
+			shiftImageInFourierTransform(F2D, F2D, (DOUBLE)ctf_dim, XX(shift), YY(shift), ZZ(shift));
+			vol().setXmippOrigin();
+   			vol().initZeros();
+   			FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(F2D)
+   			{
+   				// Take care of kp==dim/2, as XmippOrigin lies just right off center of image...
+   				if ( kp > FINISHINGZ(vol()) || ip > FINISHINGY(vol()) || jp > FINISHINGX(vol()))
+   					continue;
+   				A3D_ELEM(vol(), kp, ip, jp)    = FFTW_ELEM(F2D, kp, ip, jp).real;
+   				A3D_ELEM(vol(), -kp, -ip, -jp) = FFTW_ELEM(F2D, kp, ip, jp).real;
+   			}
+   			vol() *= (DOUBLE)ctf_dim;
+   		}
+
    		vol.write(fn_out);
     	std::cerr<<" Done writing map in "<<fn_out<<std::endl;
 
diff --git a/src/apps/refine_mpi.cpp b/src/apps/refine_mpi.cpp
index bcd1ce8..19b4668 100644
--- a/src/apps/refine_mpi.cpp
+++ b/src/apps/refine_mpi.cpp
@@ -43,15 +43,12 @@ int main(int argc, char **argv)
         {
         	optimiser.usage();
         }
-    	std::cerr << XE;
-
+    	std::cout << XE;
         MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
 
     }
 
-    MPI_Finalize();
-
-    exit(0);
+    return 0;
 
 }
 
diff --git a/src/apps/run_ctffind_mpi.cpp b/src/apps/run_ctffind_mpi.cpp
index 54327d0..425be89 100644
--- a/src/apps/run_ctffind_mpi.cpp
+++ b/src/apps/run_ctffind_mpi.cpp
@@ -35,9 +35,10 @@ int main(int argc, char *argv[])
 
     catch (RelionError XE)
     {
-        prm.usage();
+    	if (prm.verb > 0)
+    		prm.usage();
         std::cout << XE;
-        exit(1);
+        MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
     }
 
     return 0;
diff --git a/src/apps/stack_create.cpp b/src/apps/stack_create.cpp
index 7c1f226..d48b7c7 100644
--- a/src/apps/stack_create.cpp
+++ b/src/apps/stack_create.cpp
@@ -71,7 +71,7 @@ class stack_create_parameters
 		if (do_split_per_micrograph && !MD.containsLabel(EMDL_MICROGRAPH_NAME))
 			REPORT_ERROR("ERROR: Input STAR file does not contain the rlnMicrographName label");
 
-		Image<double> in;
+		Image<DOUBLE> in;
 		FileName fn_img, fn_mic;
 		std::vector<FileName> fn_mics;
 		std::vector<int> mics_ndims;
@@ -131,9 +131,9 @@ class stack_create_parameters
 
 			// Resize the output image
 			std::cout << "Resizing the output stack to "<< ndim<<" images of size: "<<xdim<<"x"<<ydim<<"x"<<zdim << std::endl;
-			double Gb = ndim*zdim*ydim*xdim*8./1024./1024./1024.;
+			DOUBLE Gb = ndim*zdim*ydim*xdim*8./1024./1024./1024.;
 			std::cout << "This will require " << Gb << "Gb of memory...."<< std::endl;
-			Image<double> out(xdim, ydim, zdim, ndim);
+			Image<DOUBLE> out(xdim, ydim, zdim, ndim);
 
 			int n = 0;
 			init_progress_bar(ndim);
@@ -153,14 +153,14 @@ class stack_create_parameters
 
 					if (do_apply_trans)
 					{
-						double xoff = 0.;
-						double yoff = 0.;
-						double psi = 0.;
+						DOUBLE xoff = 0.;
+						DOUBLE yoff = 0.;
+						DOUBLE psi = 0.;
 						MD.getValue(EMDL_ORIENT_ORIGIN_X, xoff);
 						MD.getValue(EMDL_ORIENT_ORIGIN_Y, yoff);
 						MD.getValue(EMDL_ORIENT_PSI, psi);
 						// Apply the actual transformation
-						Matrix2D<double> A;
+						Matrix2D<DOUBLE> A;
 						rotation2DMatrix(psi, A);
 					    MAT_ELEM(A,0, 2) = xoff;
 					    MAT_ELEM(A,1, 2) = yoff;
diff --git a/src/apps/star_compare.cpp b/src/apps/star_compare.cpp
new file mode 100644
index 0000000..2e29086
--- /dev/null
+++ b/src/apps/star_compare.cpp
@@ -0,0 +1,109 @@
+/***************************************************************************
+ *
+ * Author: "Sjors H.W. Scheres"
+ * MRC Laboratory of Molecular Biology
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This complete copyright notice must be included in any revised version of the
+ * source code. Additional authorship citations may be added, but existing
+ * author citations must be preserved.
+ ***************************************************************************/
+
+#include <src/image.h>
+#include <src/metadata_table.h>
+
+class star_compare_parameters
+{
+	public:
+   	FileName fn1, fn2, fn_both, fn_only1, fn_only2, fn_label1, fn_label2, fn_label3;
+	DOUBLE eps;
+   	MetaDataTable MD1, MD2, MDonly1, MDonly2, MDboth;
+	// I/O Parser
+	IOParser parser;
+
+
+	void usage()
+	{
+		parser.writeUsage(std::cerr);
+	}
+
+	void read(int argc, char **argv)
+	{
+
+		parser.setCommandLine(argc, argv);
+
+		int general_section = parser.addSection("General options");
+	    fn1 = parser.getOption("--i1", "1st input STAR file ");
+	    fn2 = parser.getOption("--i2", "2nd input STAR file ");
+	    fn_both = parser.getOption("--both", "Output STAR file with entries from both input STAR files ", "");
+	    fn_only1 = parser.getOption("--only1", "Output STAR file with entries that only occur in the 1st input STAR files ", "");
+	    fn_only2 = parser.getOption("--only2", "Output STAR file with entries that only occur in the 2nd input STAR files ", "");
+	    fn_label1 = parser.getOption("--label1", "1st metadata label for the comparison (may be string, int or DOUBLE)", "");
+	    fn_label2 = parser.getOption("--label2", "2nd metadata label for the comparison (DOUBLE only) for 2D/3D-distance)", "");
+	    fn_label3 = parser.getOption("--label3", "3rd metadata label for the comparison (DOUBLE only) for 3D-distance)", "");
+	    eps = textToFloat(parser.getOption("--max_dist", "Maximum distance to consider a match (for int and DOUBLE only)", "0."));
+
+      	// Check for errors in the command-line option
+    	if (parser.checkForErrors())
+    		REPORT_ERROR("Errors encountered on the command line, exiting...");
+	}
+
+
+	void run()
+	{
+		EMDLabel label1, label2, label3;
+		MD1.read(fn1);
+		MD2.read(fn2);
+
+		label1 = EMDL::str2Label(fn_label1);
+		label2 = (fn_label2 == "") ? EMDL_UNDEFINED : EMDL::str2Label(fn_label2);
+		label3 = (fn_label3 == "") ? EMDL_UNDEFINED : EMDL::str2Label(fn_label3);
+
+
+		compareMetaDataTable(MD1, MD2, MDboth, MDonly1, MDonly2, label1, eps, label2, label3);
+
+		std::cout << MDboth.numberOfObjects()  << " entries occur in both input STAR files." << std::endl;
+		std::cout << MDonly1.numberOfObjects() << " entries occur only in the 1st input STAR file." << std::endl;
+		std::cout << MDonly2.numberOfObjects() << " entries occur only in the 2nd input STAR file." << std::endl;
+		if (fn_both != "")
+			MDboth.write(fn_both);
+		if (fn_only1 != "")
+			MDonly1.write(fn_only1);
+		if (fn_only2 != "")
+			MDonly2.write(fn_only2);
+
+	}
+
+};
+
+
+int main(int argc, char *argv[])
+{
+	star_compare_parameters prm;
+
+	try
+    {
+
+		prm.read(argc, argv);
+
+		prm.run();
+
+    }
+    catch (RelionError XE)
+    {
+        std::cout << XE;
+        prm.usage();
+        exit(1);
+    }
+    return 0;
+}
+
diff --git a/src/apps/tiltpair_plot.cpp b/src/apps/tiltpair_plot.cpp
new file mode 100644
index 0000000..5f045f0
--- /dev/null
+++ b/src/apps/tiltpair_plot.cpp
@@ -0,0 +1,329 @@
+/***************************************************************************
+ *
+ * Author: "Sjors H.W. Scheres"
+ * MRC Laboratory of Molecular Biology
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This complete copyright notice must be included in any revised version of the
+ * source code. Additional authorship citations may be added, but existing
+ * author citations must be preserved.
+ ***************************************************************************/
+#include <src/args.h>
+#include <src/strings.h>
+#include <src/funcs.h>
+#include <src/memory.h>
+#include <src/euler.h>
+#include <src/image.h>
+#include <src/projector.h>
+#include <src/metadata_table.h>
+#include <src/fftw.h>
+#include <src/ctf.h>
+#include <src/time.h>
+#include <src/symmetries.h>
+
+
+
+class tiltpair_plot_parameters
+{
+	public:
+   	FileName fn_unt, fn_til, fn_eps, fn_sym;
+   	MetaDataTable MDu, MDt;
+   	DOUBLE exp_tilt, exp_beta, dist_from_alpha, dist_from_tilt, plot_max_tilt, plot_spot_radius;
+	// I/O Parser
+	IOParser parser;
+	SymList SL;
+	std::ofstream fh_eps;
+
+
+	void usage()
+	{
+		parser.writeUsage(std::cerr);
+	}
+
+	void read(int argc, char **argv)
+	{
+
+		parser.setCommandLine(argc, argv);
+
+		int general_section = parser.addSection("General options");
+	    fn_unt = parser.getOption("--u", "Input STAR file with untilted particles");
+	    fn_til = parser.getOption("--t", "Input STAR file with tilted particles");
+	    fn_eps = parser.getOption("--o", "Output EPS file ", "tiltpair.eps");
+	    fn_sym = parser.getOption("--sym", "Symmetry point group", "C1");
+	    exp_tilt = textToFloat(parser.getOption("--exp_tilt", "Choose symmetry operator that gives tilt angle closest to this value", "0."));
+	    exp_beta = textToFloat(parser.getOption("--exp_beta", "Choose symmetry operator that gives beta angle closest to this value", "0."));
+	    dist_from_alpha = textToFloat(parser.getOption("--dist_from_alpha", "Direction (alpha angle) of tilt axis from which to calculate distance", "0."));
+	    dist_from_tilt = textToFloat(parser.getOption("--dist_from_tilt", "Tilt angle from which to calculate distance", "0."));
+	    plot_max_tilt = textToFloat(parser.getOption("--max_tilt", "Maximum tilt angle to plot in the EPS file", "90."));
+	    plot_spot_radius = textToInteger(parser.getOption("--spot_radius", "Radius in pixels of the spots in the tiltpair plot", "3"));
+
+      	// Check for errors in the command-line option
+    	if (parser.checkForErrors())
+    		REPORT_ERROR("Errors encountered on the command line, exiting...");
+	}
+
+	void initialise()
+	{
+
+		// Get the MDs for both untilted and tilted particles
+		MDu.read(fn_unt);
+		MDt.read(fn_til);
+		if (MDu.numberOfObjects() != MDt.numberOfObjects())
+			REPORT_ERROR("Tiltpair plot ERROR: untilted and tilted STAR files have unequal number of entries.");
+
+		// Get the symmetry point group
+		int pgGroup, pgOrder;
+		SL.isSymmetryGroup(fn_sym, pgGroup, pgOrder);
+		SL.read_sym_file(fn_sym);
+
+		// Make postscript header
+		fh_eps.open(fn_eps.c_str(), std::ios::out);
+	    if (!fh_eps)
+	    	REPORT_ERROR("Tiltpair plot ERROR: Cannot open " + fn_eps + " for output");
+
+	    fh_eps << "%%!PS-Adobe-2.0\n";
+	    fh_eps << "%% Creator: Tilt pair analysis \n";
+	    fh_eps << "%% Pages: 1\n";
+	    fh_eps << "0 setgray\n";
+	    fh_eps << "0.1 setlinewidth\n";
+	    // Draw circles on postscript: 250pixels=plot_max_tilt
+	    fh_eps << "300 400 83 0 360 arc closepath stroke\n";
+	    fh_eps << "300 400 167 0 360 arc closepath stroke\n";
+	    fh_eps << "300 400 250 0 360 arc closepath stroke\n";
+	    fh_eps << "300 150 newpath moveto 300 650 lineto stroke\n";
+	    fh_eps << "50 400 newpath moveto 550 400 lineto stroke\n";
+	}
+
+	void add_to_postscript(DOUBLE tilt_angle, DOUBLE alpha, DOUBLE beta)
+	{
+
+		DOUBLE rr, th, x, y, r, g, b;
+
+		rr = (tilt_angle / plot_max_tilt)* 250;
+		x = 300. + rr * COSD(alpha);
+		y = 400. + rr * SIND(alpha);
+		value_to_redblue_scale(ABS(90.-beta), 0., 90., r, g, b);
+		fh_eps << x << " " << y << " " << plot_spot_radius << " 0 360 arc closepath "<<r<<" "<<g<<" "<<b<<" setrgbcolor fill stroke\n";
+	}
+
+	void value_to_redblue_scale(DOUBLE val, DOUBLE minF, DOUBLE maxF, DOUBLE &r, DOUBLE &g, DOUBLE &b)
+	{
+		DOUBLE diff, half;
+		half = (maxF - minF)/2.;
+		if (val < half)
+		{
+		    r=val/half;
+		    b=1.;
+		}
+		else
+		{
+		    b=(maxF-val)/half;
+		    r=1.;
+		}
+		g=0.;
+
+	}
+
+	DOUBLE check_symmetries(DOUBLE rot1, DOUBLE tilt1, DOUBLE psi1,
+	        DOUBLE &rot2, DOUBLE &tilt2, DOUBLE &psi2)
+	{
+
+	    int imax = SL.SymsNo() + 1;
+	    Matrix2D<DOUBLE>  L(4, 4), R(4, 4);  // A matrix from the list
+	    DOUBLE best_ang_dist = 3600;
+	    DOUBLE best_rot2, best_tilt2, best_psi2;
+	    DOUBLE tilt_angle, alpha, beta;
+
+	    for (int i = 0; i < imax; i++)
+	    {
+	        DOUBLE rot2p, tilt2p, psi2p;
+	        if (i == 0)
+	        {
+	            rot2p = rot2;
+	            tilt2p = tilt2;
+	            psi2p = psi2;
+	        }
+	        else
+	        {
+	            SL.get_matrices(i - 1, L, R);
+	            L.resize(3, 3); // Erase last row and column
+	            R.resize(3, 3); // as only the relative orientation is useful and not the translation
+                Euler_apply_transf(L, R, rot2, tilt2, psi2, rot2p, tilt2p, psi2p);
+	        }
+
+	        DOUBLE ang_dist = check_tilt_pairs(rot1, tilt1, psi1, rot2p, tilt2p, psi2p);
+
+	        if (ang_dist < best_ang_dist)
+	        {
+	            best_ang_dist = ang_dist;
+	            best_rot2 = rot2p;
+	            best_tilt2 = tilt2p;
+	            best_psi2 = psi2p;
+	        }
+
+	    }
+
+	    rot2 = best_rot2;
+	    tilt2 = best_tilt2;
+	    psi2 = best_psi2;
+
+	    return best_ang_dist;
+	}
+
+
+	DOUBLE check_tilt_pairs(DOUBLE rot1, DOUBLE tilt1, DOUBLE psi1,
+			DOUBLE &alpha, DOUBLE &tilt_angle, DOUBLE &beta)
+	{
+	    // Transformation matrices
+		Matrix1D<DOUBLE> axis(3);
+	    Matrix2D<DOUBLE> E1, E2;
+	    axis.resize(3);
+	    DOUBLE aux, sine_tilt_angle;
+	    DOUBLE rot2 = alpha, tilt2 = tilt_angle, psi2 = beta;
+
+	    // Calculate the transformation from one setting to the second one.
+	    Euler_angles2matrix(psi1, tilt1, rot1, E1);
+	    Euler_angles2matrix(psi2, tilt2, rot2, E2);
+	    E2 = E2 * E1.inv();
+
+	    // Get the tilt angle (and its sine)
+	    aux = ( E2(0,0) + E2(1,1) + E2(2,2) - 1. ) / 2.;
+	    if (ABS(aux) - 1. > XMIPP_EQUAL_ACCURACY)
+	    	REPORT_ERROR("BUG: aux>1");
+	    tilt_angle = ACOSD(aux);
+	    sine_tilt_angle = 2. * SIND(tilt_angle);
+
+	    // Get the tilt axis direction in angles alpha and beta
+	    if (sine_tilt_angle > XMIPP_EQUAL_ACCURACY)
+	    {
+	    	axis(0) = ( E2(2,1) - E2(1,2) ) / sine_tilt_angle;
+	    	axis(1) = ( E2(0,2) - E2(2,0) ) / sine_tilt_angle;
+	    	axis(2) = ( E2(1,0) - E2(0,1) ) / sine_tilt_angle;
+	    }
+	    else
+	    {
+	    	axis(0) = axis(1) = 0.;
+	    	axis(2) = 1.;
+	    }
+
+	    // Apply E1.inv() to the axis to get everyone in the same coordinate system again
+	    axis = E1.inv() * axis;
+
+	    // Convert to alpha and beta angle
+	    Euler_direction2angles(axis, alpha, beta);
+
+	    // Enforce positive beta: choose the other Euler angle combination to express the same direction
+	    if (beta < 0.)
+	    {
+	    	beta = -beta;
+	    	alpha+= 180.;
+	    }
+
+	    // Let alpha go from 0 to 360 degrees
+	    alpha = realWRAP(alpha, 0., 360.);
+
+
+	    // Return the value that needs to be optimized
+	    DOUBLE minimizer=0.;
+	    if (exp_beta < 999.)
+	    	minimizer = ABS(beta - exp_beta);
+	    if (exp_tilt < 999.)
+	    	minimizer += ABS(tilt_angle - exp_tilt);
+
+	    return minimizer;
+
+	}
+
+	void run()
+	{
+
+		int iline = 0;
+		FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDu)
+		{
+
+
+			// Read input data
+	        DOUBLE rot1,  tilt1,  psi1;
+	        DOUBLE rot2,  tilt2,  psi2;
+	        DOUBLE rot2p, tilt2p, psi2p;
+	        DOUBLE best_tilt, best_alpha, best_beta;
+	        DOUBLE distp;
+
+	        MDu.getValue(EMDL_ORIENT_ROT, rot1);
+	        MDt.getValue(EMDL_ORIENT_ROT, rot2, iline);
+	        MDu.getValue(EMDL_ORIENT_TILT, tilt1);
+	        MDt.getValue(EMDL_ORIENT_TILT, tilt2, iline);
+	        MDu.getValue(EMDL_ORIENT_PSI, psi1);
+	        MDt.getValue(EMDL_ORIENT_PSI, psi2, iline);
+	        iline++;
+
+	        // Bring both angles to a normalized set
+	        rot1 = realWRAP(rot1, -180, 180);
+	        tilt1 = realWRAP(tilt1, -180, 180);
+	        psi1 = realWRAP(psi1, -180, 180);
+	        rot2 = realWRAP(rot2, -180, 180);
+	        tilt2 = realWRAP(tilt2, -180, 180);
+	        psi2 = realWRAP(psi2, -180, 180);
+
+	        // Apply rotations to find the minimum distance angles
+	        rot2p = rot2;
+	        tilt2p = tilt2;
+	        psi2p = psi2;
+	        distp = check_symmetries(rot1, tilt1, psi1, rot2p, tilt2p, psi2p);
+
+	        // Calculate distance to user-defined point
+			DOUBLE xp, yp, x, y;
+			Matrix1D<DOUBLE> aux2(4);
+			xp = dist_from_tilt * COSD(dist_from_alpha);
+			yp = dist_from_tilt * SIND(dist_from_alpha);
+			x = tilt2p * COSD(rot2p);
+			y = tilt2p * SIND(rot2p);
+			aux2(3) = sqrt((xp-x)*(xp-x) + (yp-y)*(yp-y));
+			aux2(0) = tilt2p;
+			aux2(1) = rot2p;
+			aux2(2) = psi2p;
+			add_to_postscript(tilt2p, rot2p, psi2p);
+
+	    }
+
+		// Close the EPS file to write it to disk
+    	fh_eps << "showpage\n";
+        fh_eps.close();
+
+	}
+
+};
+
+
+int main(int argc, char *argv[])
+{
+	tiltpair_plot_parameters prm;
+
+	try
+    {
+
+		prm.read(argc, argv);
+
+		prm.initialise();
+
+		prm.run();
+
+    }
+    catch (RelionError XE)
+    {
+        std::cout << XE;
+        prm.usage();
+        exit(1);
+    }
+    return 0;
+}
+
diff --git a/src/assembly.cpp b/src/assembly.cpp
index 44c7471..0bb9f65 100644
--- a/src/assembly.cpp
+++ b/src/assembly.cpp
@@ -14,7 +14,7 @@ void Atom::clear()
     coords.clear();
 }
 
-Matrix1D<double> Atom::getCoordinates()
+Matrix1D<DOUBLE> Atom::getCoordinates()
 {
 	return coords;
 }
@@ -26,7 +26,7 @@ void Residue::clear()
 	atoms.clear();
 }
 
-long int Residue::addAtom(std::string atomname, double x, double y, double z, double occ, double bfac)
+long int Residue::addAtom(std::string atomname, DOUBLE x, DOUBLE y, DOUBLE z, DOUBLE occ, DOUBLE bfac)
 {
 	Atom atom(atomname);
 	atom.coords = vectorR3(x,y,z);
@@ -403,7 +403,7 @@ void Assembly::sortResidues()
 
 }
 
-void Assembly::applyTransformation(Matrix2D<double> &mat, Matrix1D<double> &shift)
+void Assembly::applyTransformation(Matrix2D<DOUBLE> &mat, Matrix1D<DOUBLE> &shift)
 {
 	for (int imol = 0; imol < molecules.size(); imol++)
 	{
diff --git a/src/assembly.h b/src/assembly.h
index 7d5b090..d3015f0 100644
--- a/src/assembly.h
+++ b/src/assembly.h
@@ -35,13 +35,13 @@ public:
         std::string name;
 
         // Coordinates
-        Matrix1D<double> coords;
+        Matrix1D<DOUBLE> coords;
 
         // Occupancy
-        double occupancy;
+        DOUBLE occupancy;
 
         // B-factor
-        double bfactor;
+        DOUBLE bfactor;
 
         // Empty constructor
         Atom()
@@ -66,7 +66,7 @@ public:
         void clear();
 
         // Get the 3D corrdinates as a POint3D
-        Matrix1D<double> getCoordinates();
+        Matrix1D<DOUBLE> getCoordinates();
 };
 
 
@@ -106,7 +106,7 @@ public:
         void clear();
 
         // Add an Atom to this Residue;
-        long int addAtom(std::string atomname, double x, double y, double z, double occ = 1.0, double bfac = 0.0);
+        long int addAtom(std::string atomname, DOUBLE x, DOUBLE y, DOUBLE z, DOUBLE occ = 1.0, DOUBLE bfac = 0.0);
 
         int numberOfAtoms()
         {
@@ -245,7 +245,7 @@ public:
         void checkBreaksInResidueNumbering(int maximum_residue_break = 500);
 
         // Apply a transformation (first rotation, then shift)
-        void applyTransformation(Matrix2D<double> &mat, Matrix1D<double> &shift);
+        void applyTransformation(Matrix2D<DOUBLE> &mat, Matrix1D<DOUBLE> &shift);
 
 };
 
diff --git a/src/autopicker.cpp b/src/autopicker.cpp
index 814d7d2..d3ffd52 100644
--- a/src/autopicker.cpp
+++ b/src/autopicker.cpp
@@ -30,6 +30,8 @@ void AutoPicker::read(int argc, char **argv)
 	fn_out = parser.getOption("--o", "Output rootname", "autopick");
 	angpix = textToFloat(parser.getOption("--angpix", "Pixel size in Angstroms"));
 	particle_diameter = textToFloat(parser.getOption("--particle_diameter", "Diameter of the circular mask that will be applied to the experimental images (in Angstroms)"));
+	decrease_radius = textToInteger(parser.getOption("--shrink_particle_mask", "Shrink the particle mask by this many pixels (to detect Einstein-from-noise classes)", "2"));
+	outlier_removal_zscore= textToFloat(parser.getOption("--outlier_removal_zscore", "Remove pixels that are this many sigma away from the mean", "8."));
 	do_write_fom_maps = parser.checkOption("--write_fom_maps", "Write calculated probability-ratio maps to disc (for re-reading in subsequent runs)");
 	do_read_fom_maps = parser.checkOption("--read_fom_maps", "Skip probability calculations, re-read precalculated maps from disc");
 
@@ -44,8 +46,10 @@ void AutoPicker::read(int argc, char **argv)
 	int peak_section = parser.addSection("Peak-search options");
 	min_fraction_expected_Pratio = textToFloat(parser.getOption("--threshold", "Fraction of expected probability ratio in order to consider peaks?", "0.25"));
 	min_particle_distance = textToFloat(parser.getOption("--min_distance", "Minimum distance (in A) between any two particles (default is half the box size)","-1"));
+	max_stddev_noise = textToFloat(parser.getOption("--max_stddev_noise", "Maximum standard deviation in the noise area to use for picking peaks (default is no maximum)","-1"));
 	autopick_skip_side = textToInteger(parser.getOption("--skip_side", "Keep this many extra pixels (apart from particle_size/2) away from the edge of the micrograph ","0"));
 
+
 	int expert_section = parser.addSection("Expert options");
 	verb = textToInteger(parser.getOption("--verb", "Verbosity", "1"));
 
@@ -103,7 +107,7 @@ void AutoPicker::initialise()
 		FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDref)
 		{
 			// Get all reference images and their names
-			Image<double> Iref;
+			Image<DOUBLE> Iref;
 
 			FileName fn_img;
 			if (!MDref.getValue(EMDL_MLMODEL_REF_IMAGE, fn_img))
@@ -121,7 +125,7 @@ void AutoPicker::initialise()
 	}
 	else
 	{
-		Image<double> Istk, Iref;
+		Image<DOUBLE> Istk, Iref;
 		Istk.read(fn_ref);
 		for (int n = 0; n < NSIZE(Istk()); n++)
 		{
@@ -136,6 +140,7 @@ void AutoPicker::initialise()
 
 	// Get the squared particle radius (in integer pixels)
 	particle_radius2 = ROUND(particle_diameter/(2. * angpix));
+	particle_radius2 -= decrease_radius;
 	particle_radius2*= particle_radius2;
 #ifdef DEBUG
 	std::cerr << " particle_size= " << particle_size << " sqrt(particle_radius2)= " << sqrt(particle_radius2) << std::endl;
@@ -150,7 +155,7 @@ void AutoPicker::initialise()
 	}
 
 	// Get micrograph_size
-	Image<double> Imic;
+	Image<DOUBLE> Imic;
 	Imic.read(fn_micrographs[0], false);
 	micrograph_xsize = XSIZE(Imic());
 	micrograph_ysize = YSIZE(Imic());
@@ -175,8 +180,8 @@ void AutoPicker::initialise()
 	{
 		// Calculate a circular mask based on the particle_diameter and then store its FT
 		FourierTransformer transformer;
-		MultidimArray<double> Mcirc_mask(particle_size, particle_size);
-		MultidimArray<double> Maux(micrograph_size, micrograph_size);
+		MultidimArray<DOUBLE> Mcirc_mask(particle_size, particle_size);
+		MultidimArray<DOUBLE> Maux(micrograph_size, micrograph_size);
 		Mcirc_mask.setXmippOrigin();
 		Maux.setXmippOrigin();
 
@@ -228,7 +233,7 @@ void AutoPicker::initialise()
 
 		PPref.clear();
 		Projector PP(micrograph_size);
-		MultidimArray<double> dummy;
+		MultidimArray<DOUBLE> dummy;
 
 		// TODO!!! sum_ref etc should be CTF-dependent!!!
 		//sum_ref_under_circ_mask.clear();
@@ -291,18 +296,18 @@ void AutoPicker::run()
 
 void AutoPicker::autoPickOneMicrograph(FileName &fn_mic)
 {
-	Image<double> Imic;
+	Image<DOUBLE> Imic;
 	MultidimArray<Complex > Faux, Faux2, Fmic;
-	MultidimArray<double> Maux, Mstddev, Mmean, Mdiff2, MsumX2, Mccf_best, Mpsi_best, Fctf;
+	MultidimArray<DOUBLE> Maux, Mstddev, Mmean, Mdiff2, MsumX2, Mccf_best, Mpsi_best, Fctf;
 	FourierTransformer transformer;
-	double sum_ref_under_circ_mask, sum_ref2_under_circ_mask;
+	DOUBLE sum_ref_under_circ_mask, sum_ref2_under_circ_mask;
 	int my_skip_side = autopick_skip_side + particle_size/2;
 	CTF ctf;
 
 	int min_distance_pix = ROUND(min_particle_distance / angpix);
 
 #ifdef DEBUG
-	Image<double> tt;
+	Image<DOUBLE> tt;
 	tt().resize(micrograph_size, micrograph_size);
 	std::cerr << " fn_mic= " << fn_mic << std::endl;
 #endif
@@ -311,7 +316,7 @@ void AutoPicker::autoPickOneMicrograph(FileName &fn_mic)
 	Imic().setXmippOrigin();
 
 	// Let's just check the square size again....
-	double my_size, my_xsize, my_ysize;
+	DOUBLE my_size, my_xsize, my_ysize;
 	my_xsize = XSIZE(Imic());
 	my_ysize = YSIZE(Imic());
 	my_size = (my_xsize != my_ysize) ? XMIPP_MAX(my_xsize, my_ysize) : my_xsize;
@@ -324,7 +329,16 @@ void AutoPicker::autoPickOneMicrograph(FileName &fn_mic)
 	}
 
 	// Set mean to zero and stddev to 1 to prevent numerical problems with one-sweep stddev calculations....
-	Imic().statisticsAdjust(0., 1.);
+    DOUBLE avg0, stddev0, minval0, maxval0;
+	Imic().computeStats(avg0, stddev0, minval0, maxval0);
+	FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Imic())
+	{
+		// Remove pixel values that are too far away from the mean
+		if ( ABS(DIRECT_MULTIDIM_ELEM(Imic(), n) - avg0) / stddev0 > outlier_removal_zscore)
+			DIRECT_MULTIDIM_ELEM(Imic(), n) = avg0;
+
+		DIRECT_MULTIDIM_ELEM(Imic(), n) = (DIRECT_MULTIDIM_ELEM(Imic(), n) - avg0) / stddev0;
+	}
 
 	if (micrograph_xsize != micrograph_ysize)
 	{
@@ -360,7 +374,7 @@ void AutoPicker::autoPickOneMicrograph(FileName &fn_mic)
 		}
 #ifdef DEBUG
 		std::cerr << " Read CTF info from" << fn_mic.withoutExtension()<<"_ctf.star" << std::endl;
-		Image<double> Ictf;
+		Image<DOUBLE> Ictf;
 		Ictf()=Fctf;
 		Ictf.write("Mmic_ctf.spi");
 #endif
@@ -369,8 +383,15 @@ void AutoPicker::autoPickOneMicrograph(FileName &fn_mic)
 	Mccf_best.resize(micrograph_size, micrograph_size);
 	Mpsi_best.resize(micrograph_size, micrograph_size);
 
-	double normfft = (double)(micrograph_size * micrograph_size) / (double)nr_pixels_circular_mask;;
-	if (!do_read_fom_maps)
+	DOUBLE normfft = (DOUBLE)(micrograph_size * micrograph_size) / (DOUBLE)nr_pixels_circular_mask;;
+	if (do_read_fom_maps)
+	{
+		FileName fn_tmp=fn_mic.withoutExtension()+"_"+fn_out+"_stddevNoise.spi";
+		Image<DOUBLE> It;
+		It.read(fn_tmp);
+		Mstddev = It();
+	}
+	else
 	{
 		/*
 		 * Squared difference FOM:
@@ -421,6 +442,15 @@ void AutoPicker::autoPickOneMicrograph(FileName &fn_mic)
 		// The following calculate mu and sig under the solvent area at every position in the micrograph
 		calculateStddevAndMeanUnderMask(Fmic, Fmic2, Finvmsk, nr_pixels_circular_invmask, Mstddev, Mmean);
 
+		if (do_write_fom_maps)
+		{
+			// TMP output
+			FileName fn_tmp=fn_mic.withoutExtension()+"_"+fn_out+"_stddevNoise.spi";
+			Image<DOUBLE> It;
+			It() = Mstddev;
+			It.write(fn_tmp);
+		}
+
 		// From now on use downsized Fmic, as the cross-correlation with the references can be done at lower resolution
 		windowFourierTransform(Fmic, Faux, downsize_mic);
 		Fmic = Faux;
@@ -433,11 +463,11 @@ void AutoPicker::autoPickOneMicrograph(FileName &fn_mic)
 	peaks.clear();
 	for (int iref = 0; iref < Mrefs.size(); iref++)
 	{
-		double expected_Pratio; // the expectedFOM for this (ctf-corrected) reference
+		DOUBLE expected_Pratio; // the expectedFOM for this (ctf-corrected) reference
 		if (do_read_fom_maps)
 		{
 			FileName fn_tmp;
-			Image<double> It;
+			Image<DOUBLE> It;
 			fn_tmp.compose(fn_mic.withoutExtension()+"_"+fn_out+"_ref", iref,"_bestCCF.spi");
 			It.read(fn_tmp);
 			Mccf_best = It();
@@ -452,11 +482,11 @@ void AutoPicker::autoPickOneMicrograph(FileName &fn_mic)
 		{
 			Mccf_best.initConstant(-99.e99);
 			bool is_first_psi = true;
-			for (double psi = 0. ; psi < 360.; psi+=psi_sampling)
+			for (DOUBLE psi = 0. ; psi < 360.; psi+=psi_sampling)
 			{
 
 				// Get the Euler matrix
-				Matrix2D<double> A(3,3);
+				Matrix2D<DOUBLE> A(3,3);
 				Euler_angles2matrix(0., 0., psi, A);
 
 				// Now get the FT of the rotated (non-ctf-corrected) template
@@ -513,9 +543,9 @@ void AutoPicker::autoPickOneMicrograph(FileName &fn_mic)
 
 					sum_ref_under_circ_mask = 0.;
 					sum_ref2_under_circ_mask = 0.;
-					double suma2 = 0.;
-					double sumn = 1.;
-					MultidimArray<double> Mctfref(particle_size, particle_size);
+					DOUBLE suma2 = 0.;
+					DOUBLE sumn = 1.;
+					MultidimArray<DOUBLE> Mctfref(particle_size, particle_size);
 					Mctfref.setXmippOrigin();
 					FOR_ALL_ELEMENTS_IN_ARRAY2D(Mctfref) // only loop over smaller Mctfref, but take values from large Maux!
 					{
@@ -561,7 +591,7 @@ void AutoPicker::autoPickOneMicrograph(FileName &fn_mic)
 				// Still to do (per reference): - 2/sig*Sum(AX) + 2*mu/sig*Sum(A) + Sum(A^2)
 				FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Maux)
 				{
-					double diff2 = - 2. * normfft * DIRECT_MULTIDIM_ELEM(Maux, n);
+					DOUBLE diff2 = - 2. * normfft * DIRECT_MULTIDIM_ELEM(Maux, n);
 					diff2 += 2. * DIRECT_MULTIDIM_ELEM(Mmean, n) * sum_ref_under_circ_mask;
 					if (DIRECT_MULTIDIM_ELEM(Mstddev, n) > 1E-10)
 						diff2 /= DIRECT_MULTIDIM_ELEM(Mstddev, n);
@@ -609,7 +639,7 @@ void AutoPicker::autoPickOneMicrograph(FileName &fn_mic)
 			{
 				// TMP output
 				FileName fn_tmp;
-				Image<double> It;
+				Image<DOUBLE> It;
 				It() = Mccf_best;
 				// Store expected_Pratio in the header of the image..
 				It.MDMainHeader.setValue(EMDL_IMAGE_STATS_MAX, expected_Pratio);;
@@ -625,9 +655,10 @@ void AutoPicker::autoPickOneMicrograph(FileName &fn_mic)
 
 		// Now that we have Mccf_best and Mpsi_best, get the peaks
 		std::vector<Peak> my_ref_peaks;
+		Mstddev.setXmippOrigin();
 		Mccf_best.setXmippOrigin();
 		Mpsi_best.setXmippOrigin();
-		peakSearch(Mccf_best, Mpsi_best, iref, my_skip_side, my_ref_peaks);
+		peakSearch(Mccf_best, Mpsi_best, Mstddev, iref, my_skip_side, my_ref_peaks);
 
 		prunePeakClusters(my_ref_peaks, min_distance_pix);
 
@@ -648,8 +679,8 @@ void AutoPicker::autoPickOneMicrograph(FileName &fn_mic)
 	for (int ipeak =0; ipeak < peaks.size(); ipeak++)
 	{
 		MDout.addObject();
-		MDout.setValue(EMDL_IMAGE_COORD_X, (double)(peaks[ipeak].x));
-		MDout.setValue(EMDL_IMAGE_COORD_Y, (double)(peaks[ipeak].y));
+		MDout.setValue(EMDL_IMAGE_COORD_X, (DOUBLE)(peaks[ipeak].x));
+		MDout.setValue(EMDL_IMAGE_COORD_Y, (DOUBLE)(peaks[ipeak].y));
 		MDout.setValue(EMDL_ORIENT_PSI, peaks[ipeak].psi);
 		MDout.setValue(EMDL_PARTICLE_CLASS, peaks[ipeak].ref + 1); // start counting at 1
 		MDout.setValue(EMDL_PARTICLE_AUTOPICK_FOM, peaks[ipeak].fom);
@@ -660,20 +691,20 @@ void AutoPicker::autoPickOneMicrograph(FileName &fn_mic)
 }
 
 void AutoPicker::calculateStddevAndMeanUnderMask(const MultidimArray<Complex > &_Fmic, const MultidimArray<Complex > &_Fmic2,
-		MultidimArray<Complex > &_Fmsk, int nr_nonzero_pixels_mask, MultidimArray<double> &_Mstddev, MultidimArray<double> &_Mmean)
+		MultidimArray<Complex > &_Fmsk, int nr_nonzero_pixels_mask, MultidimArray<DOUBLE> &_Mstddev, MultidimArray<DOUBLE> &_Mmean)
 {
 
 	MultidimArray<Complex > Faux, Faux2;
-	MultidimArray<double> Maux(micrograph_size, micrograph_size);
+	MultidimArray<DOUBLE> Maux(micrograph_size, micrograph_size);
 	FourierTransformer transformer;
 
 	_Mstddev.initZeros(micrograph_size, micrograph_size);
-	double normfft = (double)(micrograph_size * micrograph_size) / (double)nr_nonzero_pixels_mask;
+	DOUBLE normfft = (DOUBLE)(micrograph_size * micrograph_size) / (DOUBLE)nr_nonzero_pixels_mask;
 
 	// Calculate convolution of micrograph and mask, to get average under mask at all points
 	Faux.resize(_Fmic);
 #ifdef DEBUG
-	Image<double> tt;
+	Image<DOUBLE> tt;
 #endif
 
 	FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Faux)
@@ -726,7 +757,7 @@ void AutoPicker::calculateStddevAndMeanUnderMask(const MultidimArray<Complex > &
 
 }
 
-void AutoPicker::peakSearch(const MultidimArray<double> &Mfom, const MultidimArray<double> &Mpsi, int iref,
+void AutoPicker::peakSearch(const MultidimArray<DOUBLE> &Mfom, const MultidimArray<DOUBLE> &Mpsi, const MultidimArray<DOUBLE> &Mstddev, int iref,
 		int skip_side, std::vector<Peak> &peaks)
 {
 
@@ -742,10 +773,15 @@ void AutoPicker::peakSearch(const MultidimArray<double> &Mfom, const MultidimArr
 		for (int j = FIRST_XMIPP_INDEX(micrograph_xsize) + skip_side; j <= LAST_XMIPP_INDEX(micrograph_xsize) - skip_side; j++)
 		{
 
-			double myval = A2D_ELEM(Mfom, i, j);
+			DOUBLE myval = A2D_ELEM(Mfom, i, j);
 			// check if this element is above the threshold
 			if (myval  >= min_fraction_expected_Pratio)
 			{
+
+				// Only check stddev in the noise areas if max_stddev_noise is positive!
+				if (max_stddev_noise > 0. && A2D_ELEM(Mstddev, i, j) > max_stddev_noise)
+					continue;
+
 				// This is a peak if all four neighbours are also above the threshold, AND have lower values than myval
 				if (A2D_ELEM(Mfom, i-1, j) < min_fraction_expected_Pratio || A2D_ELEM(Mfom, i-1, j) > myval )
 					continue;
@@ -803,7 +839,7 @@ void AutoPicker::prunePeakClusters(std::vector<Peak> &peaks, int min_distance)
 		int ipass = 0;
 		while (cluster.size() > 0)
 		{
-			double best_relative_fom=-1.;
+			DOUBLE best_relative_fom=-1.;
 			Peak bestpeak;
 			for (int iclus = 0; iclus < cluster.size(); iclus++)
 			{
diff --git a/src/autopicker.h b/src/autopicker.h
index eda96ad..ac5d22c 100644
--- a/src/autopicker.h
+++ b/src/autopicker.h
@@ -21,9 +21,9 @@ struct Peak
 	int x;
 	int y;
 	int ref;
-	double psi;
-	double fom;
-	double relative_fom;
+	DOUBLE psi;
+	DOUBLE fom;
+	DOUBLE relative_fom;
 };
 
 class AutoPicker
@@ -40,17 +40,17 @@ public:
 	FileName fn_in, fn_ref, fns_autopick, fn_out;
 
 	// Pixel size (for low-pass filter and particle diameter)
-	double angpix;
+	DOUBLE angpix;
 
 	// Metadata of the micrographs
 	MetaDataTable MDmic;
 
 	// Particle diameter (in Angstroms)
-	double particle_diameter;
-	int particle_radius2;
+	DOUBLE particle_diameter;
+	int particle_radius2, decrease_radius;
 
 	// Low pass filetr cutoff (in Angstroms)
-	double lowpass;
+	DOUBLE lowpass;
 
 	// Original size of the reference images
 	int particle_size;
@@ -59,7 +59,7 @@ public:
 	int current_size;
 
 	// Vector with all original reference images
-	std::vector<MultidimArray<double> > Mrefs;
+	std::vector<MultidimArray<DOUBLE> > Mrefs;
 
 	// FTs of the reference images (either for autopicking or for feature calculation)
 	std::vector<Projector > PPref;
@@ -91,13 +91,19 @@ public:
 	int autopick_skip_side;
 
 	// In-plane rotational sampling (in degrees)
-	double psi_sampling;
+	DOUBLE psi_sampling;
 
 	// Fraction of expected probability ratio to consider as peaks
-	double min_fraction_expected_Pratio;
+	DOUBLE min_fraction_expected_Pratio;
 
 	// Number of Angstroms any 2 particle peaks need to be apart
-	double min_particle_distance;
+	DOUBLE min_particle_distance;
+
+	// Maximum standard deviation of the noise prior to normalization to pick peaks from
+	DOUBLE max_stddev_noise;
+
+	// Removal of outlier pixel values
+	DOUBLE outlier_removal_zscore;
 
 	// Size of the downsize micrographs for autopicking
 	int downsize_mic;
@@ -129,10 +135,10 @@ private:
 	// The FFTs of the micrograph (Fmic), micrograph-squared (Fmic2) and the mask (Fmsk) need to be provided at downsize_mic
 	// The putput (Mstddev) will be at (binned) micrograph_size
 	void calculateStddevAndMeanUnderMask(const MultidimArray<Complex > &Fmic, const MultidimArray<Complex > &Fmic2,
-			MultidimArray<Complex > &Fmsk, int nr_nonzero_pixels_mask, MultidimArray<double> &Mstddev, MultidimArray<double> &Mmean);
+			MultidimArray<Complex > &Fmsk, int nr_nonzero_pixels_mask, MultidimArray<DOUBLE> &Mstddev, MultidimArray<DOUBLE> &Mmean);
 
 	// Peak search for all pixels above a given threshold in the map
-	void peakSearch(const MultidimArray<double> &Mccf, const MultidimArray<double> &Mpsi, int iref, int skip_side, std::vector<Peak> &peaks);
+	void peakSearch(const MultidimArray<DOUBLE> &Mccf, const MultidimArray<DOUBLE> &Mpsi, const MultidimArray<DOUBLE> &Mstddev, int iref, int skip_side, std::vector<Peak> &peaks);
 
 	// Now prune the coordinates: within min_particle_distance: all peaks are the same cluster
 	// From each cluster, take the single peaks with the highest ccf
diff --git a/src/backprojector.cpp b/src/backprojector.cpp
index b8e3869..d80cb1f 100644
--- a/src/backprojector.cpp
+++ b/src/backprojector.cpp
@@ -43,16 +43,16 @@ void BackProjector::initZeros(int current_size)
 }
 
 void BackProjector::backproject(const MultidimArray<Complex > &f2d,
-		                        const Matrix2D<double> &A, bool inv,
-		                        const MultidimArray<double> *Mweight)
+		                        const Matrix2D<DOUBLE> &A, bool inv,
+		                        const MultidimArray<DOUBLE> *Mweight)
 {
-	double fx, fy, fz, mfx, mfy, mfz, xp, yp, zp;
+	DOUBLE fx, fy, fz, mfx, mfy, mfz, xp, yp, zp;
 	int first_x, x0, x1, y0, y1, z0, z1, y, y2, r2;
 	bool is_neg_x;
-	double dd000, dd001, dd010, dd011, dd100, dd101, dd110, dd111;
+	DOUBLE dd000, dd001, dd010, dd011, dd100, dd101, dd110, dd111;
 	Complex my_val;
-	Matrix2D<double> Ainv;
-	double my_weight = 1.;
+	Matrix2D<DOUBLE> Ainv;
+	DOUBLE my_weight = 1.;
 
 	// f2d should already be in the right size (ori_size,orihalfdim)
     // AND the points outside max_r should already be zero...
@@ -64,7 +64,7 @@ void BackProjector::backproject(const MultidimArray<Complex > &f2d,
     	Ainv = A.transpose();
 
     // Go from the 2D slice coordinates to the 3D coordinates
-    Ainv *= (double)padding_factor;  // take scaling into account directly
+    Ainv *= (DOUBLE)padding_factor;  // take scaling into account directly
     int max_r2 = r_max * r_max;
     int min_r2_nn = r_min_nn * r_min_nn;
 
@@ -221,16 +221,16 @@ void BackProjector::backproject(const MultidimArray<Complex > &f2d,
 }
 
 void BackProjector::backrotate2D(const MultidimArray<Complex > &f2d,
-		                         const Matrix2D<double> &A, bool inv,
-		                         const MultidimArray<double> *Mweight)
+		                         const Matrix2D<DOUBLE> &A, bool inv,
+		                         const MultidimArray<DOUBLE> *Mweight)
 {
-	double fx, fy, mfx, mfy, xp, yp;
+	DOUBLE fx, fy, mfx, mfy, xp, yp;
 	int first_x, x0, x1, y0, y1, y, y2, r2;
 	bool is_neg_x;
-	double dd00, dd01, dd10, dd11;
+	DOUBLE dd00, dd01, dd10, dd11;
 	Complex my_val;
-	Matrix2D<double> Ainv;
-	double my_weight = 1.;
+	Matrix2D<DOUBLE> Ainv;
+	DOUBLE my_weight = 1.;
 
 	// f2d should already be in the right size (ori_size,orihalfdim)
     // AND the points outside max_r should already be zero...
@@ -242,7 +242,7 @@ void BackProjector::backrotate2D(const MultidimArray<Complex > &f2d,
     	Ainv = A.transpose();
 
     // Go from the 2D slice coordinates to the data-array coordinates
-    Ainv *= (double)padding_factor;  // take scaling into account directly
+    Ainv *= (DOUBLE)padding_factor;  // take scaling into account directly
     int max_r2 = r_max * r_max;
     int min_r2_nn = r_min_nn * r_min_nn;
 
@@ -373,7 +373,200 @@ void BackProjector::backrotate2D(const MultidimArray<Complex > &f2d,
 	} // endif y-loop
 }
 
-void BackProjector::getLowResDataAndWeight(MultidimArray<Complex > &lowres_data, MultidimArray<double> &lowres_weight,
+void BackProjector::backrotate3D(const MultidimArray<Complex > &f3d,
+		                         const Matrix2D<DOUBLE> &A, bool inv,
+		                         const MultidimArray<DOUBLE> *Mweight)
+{
+	DOUBLE fx, fy, fz, mfx, mfy, mfz, xp, yp, zp;
+	int first_x, x0, x1, y0, y1, z0, z1, y, y2, z, z2, r2;
+	bool is_neg_x;
+	DOUBLE dd000, dd010, dd100, dd110, dd001, dd011, dd101, dd111;
+	Complex my_val;
+	Matrix2D<DOUBLE> Ainv;
+	DOUBLE my_weight = 1.;
+
+	// f3d should already be in the right size (ori_size,orihalfdim)
+    // AND the points outside max_r should already be zero...
+
+	// Use the inverse matrix
+    if (inv)
+    	Ainv = A;
+    else
+    	Ainv = A.transpose();
+
+    // Go from the 2D slice coordinates to the data-array coordinates
+    Ainv *= (DOUBLE)padding_factor;  // take scaling into account directly
+    int max_r2 = r_max * r_max;
+    int min_r2_nn = r_min_nn * r_min_nn;
+
+//#define DEBUG_BACKROTATE
+#ifdef DEBUG_BACKROTATE
+    std::cerr << " XSIZE(f3d)= "<< XSIZE(f3d) << std::endl;
+    std::cerr << " YSIZE(f3d)= "<< YSIZE(f3d) << std::endl;
+    std::cerr << " XSIZE(data)= "<< XSIZE(data) << std::endl;
+    std::cerr << " YSIZE(data)= "<< YSIZE(data) << std::endl;
+    std::cerr << " STARTINGX(data)= "<< STARTINGX(data) << std::endl;
+    std::cerr << " STARTINGY(data)= "<< STARTINGY(data) << std::endl;
+    std::cerr << " STARTINGZ(data)= "<< STARTINGZ(data) << std::endl;
+    std::cerr << " max_r= "<< r_max << std::endl;
+    std::cerr << " Ainv= " << Ainv << std::endl;
+#endif
+
+    for (int k=0; k < ZSIZE(f3d); k++)
+	{
+		// Don't search beyond square with side max_r
+		if (k <= r_max)
+		{
+			z = k;
+			first_x = 0;
+		}
+		else if (k >= YSIZE(f3d) - r_max)
+		{
+			z = k - YSIZE(f3d);
+			/// TODO: still check this better in the 3D case!!!
+			// x==0 (y,z)-plane is stored twice in the FFTW format. Don't set it twice in BACKPROJECTION!
+			first_x = 1;
+		}
+		else
+			continue;
+
+		z2 = z * z;
+		for (int i=0; i < YSIZE(f3d); i++)
+		{
+			// Don't search beyond square with side max_r
+			if (i <= r_max)
+			{
+				y = i;
+			}
+			else if (i >= YSIZE(f3d) - r_max)
+			{
+				y = i - YSIZE(f3d);
+			}
+			else
+				continue;
+
+			y2 = y * y;
+			for (int x = first_x; x <= r_max; x++)
+			{
+				// Only include points with radius < max_r (exclude points outside circle in square)
+				r2 = x * x + y2 + z2;
+				if (r2 > max_r2)
+					continue;
+
+				// Get the relevant value in the input image
+				my_val = DIRECT_A3D_ELEM(f3d, k, i, x);
+
+				// Get the weight
+				if (Mweight != NULL)
+					my_weight = DIRECT_A3D_ELEM(*Mweight, k, i, x);
+				// else: my_weight was already initialised to 1.
+
+				if (my_weight > 0.)
+				{
+					// Get logical coordinates in the 3D map
+					xp = Ainv(0,0) * x + Ainv(0,1) * y + Ainv(0,2) * z;
+					yp = Ainv(1,0) * x + Ainv(1,1) * y + Ainv(1,2) * z;
+					zp = Ainv(2,0) * x + Ainv(2,1) * y + Ainv(2,2) * z;
+
+					if (interpolator == TRILINEAR || r2 < min_r2_nn)
+					{
+						// Only asymmetric half is stored
+						if (xp < 0)
+						{
+							// Get complex conjugated hermitian symmetry pair
+							xp = -xp;
+							yp = -yp;
+							zp = -zp;
+							is_neg_x = true;
+						}
+						else
+						{
+							is_neg_x = false;
+						}
+
+						// Trilinear interpolation (with physical coords)
+						// Subtract STARTINGY to accelerate access to data (STARTINGX=0)
+						// In that way use DIRECT_A3D_ELEM, rather than A3D_ELEM
+						x0 = FLOOR(xp);
+						fx = xp - x0;
+						x1 = x0 + 1;
+
+						y0 = FLOOR(yp);
+						fy = yp - y0;
+						y0 -=  STARTINGY(data);
+						y1 = y0 + 1;
+
+						z0 = FLOOR(zp);
+						fz = zp - z0;
+						z0 -=  STARTINGZ(data);
+						z1 = z0 + 1;
+
+						mfx = 1. - fx;
+						mfy = 1. - fy;
+						mfz = 1. - fz;
+
+						dd000 = mfz * mfy * mfx;
+						dd001 = mfz * mfy *  fx;
+						dd010 = mfz *  fy * mfx;
+						dd011 = mfz *  fy *  fx;
+						dd100 =  fz * mfy * mfx;
+						dd101 =  fz * mfy *  fx;
+						dd110 =  fz *  fy * mfx;
+						dd111 =  fz *  fy *  fx;
+
+						if (is_neg_x)
+							my_val = conj(my_val);
+
+						// Store slice in 3D weighted sum
+						DIRECT_A3D_ELEM(data, z0, y0, x0) += dd000 * my_val;
+						DIRECT_A3D_ELEM(data, z0, y0, x1) += dd001 * my_val;
+						DIRECT_A3D_ELEM(data, z0, y1, x0) += dd010 * my_val;
+						DIRECT_A3D_ELEM(data, z0, y1, x1) += dd011 * my_val;
+						DIRECT_A3D_ELEM(data, z1, y0, x0) += dd100 * my_val;
+						DIRECT_A3D_ELEM(data, z1, y0, x1) += dd101 * my_val;
+						DIRECT_A3D_ELEM(data, z1, y1, x0) += dd110 * my_val;
+						DIRECT_A3D_ELEM(data, z1, y1, x1) += dd111 * my_val;
+						// Store corresponding weights
+						DIRECT_A3D_ELEM(weight, z0, y0, x0) += dd000 * my_weight;
+						DIRECT_A3D_ELEM(weight, z0, y0, x1) += dd001 * my_weight;
+						DIRECT_A3D_ELEM(weight, z0, y1, x0) += dd010 * my_weight;
+						DIRECT_A3D_ELEM(weight, z0, y1, x1) += dd011 * my_weight;
+						DIRECT_A3D_ELEM(weight, z1, y0, x0) += dd100 * my_weight;
+						DIRECT_A3D_ELEM(weight, z1, y0, x1) += dd101 * my_weight;
+						DIRECT_A3D_ELEM(weight, z1, y1, x0) += dd110 * my_weight;
+						DIRECT_A3D_ELEM(weight, z1, y1, x1) += dd111 * my_weight;
+
+
+					} // endif TRILINEAR
+					else if (interpolator == NEAREST_NEIGHBOUR )
+					{
+						x0 = ROUND(xp);
+						y0 = ROUND(yp);
+						z0 = ROUND(zp);
+
+						if (x0 < 0)
+						{
+							A3D_ELEM(data, -z0, -y0, -x0) += conj(my_val);
+							A3D_ELEM(weight, -z0, -y0, -x0) += my_weight;
+						}
+						else
+						{
+							A3D_ELEM(data, z0, y0, x0) += my_val;
+							A3D_ELEM(weight, z0, y0, x0) += my_weight;
+						}
+
+					} // endif NEAREST_NEIGHBOUR
+					else
+					{
+						REPORT_ERROR("BackProjector::backrotate3D%%ERROR: unrecognized interpolator ");
+					}
+				} // endif weight > 0.
+			} // endif x-loop
+		} // endif y-loop
+	} // endif z-loop
+}
+
+void BackProjector::getLowResDataAndWeight(MultidimArray<Complex > &lowres_data, MultidimArray<DOUBLE> &lowres_weight,
 		int lowres_r_max)
 {
 
@@ -410,7 +603,7 @@ void BackProjector::getLowResDataAndWeight(MultidimArray<Complex > &lowres_data,
 
 }
 
-void BackProjector::setLowResDataAndWeight(MultidimArray<Complex > &lowres_data, MultidimArray<double> &lowres_weight,
+void BackProjector::setLowResDataAndWeight(MultidimArray<Complex > &lowres_data, MultidimArray<DOUBLE> &lowres_weight,
 		int lowres_r_max)
 {
 
@@ -452,7 +645,7 @@ void BackProjector::setLowResDataAndWeight(MultidimArray<Complex > &lowres_data,
 
 void BackProjector::getDownsampledAverage(MultidimArray<Complex > &avg)
 {
-	MultidimArray<double> down_weight;
+	MultidimArray<DOUBLE> down_weight;
 
 	// Pre-set down_data and down_weight sizes
 	int down_size = 2 * (r_max + 1) + 1;
@@ -479,9 +672,9 @@ void BackProjector::getDownsampledAverage(MultidimArray<Complex > &avg)
 	int kp, ip, jp;
 	FOR_ALL_ELEMENTS_IN_ARRAY3D(data)
 	{
-		kp = ROUND((double)k/padding_factor);
-		ip = ROUND((double)i/padding_factor);
-		jp = ROUND((double)j/padding_factor);
+		kp = ROUND((DOUBLE)k/padding_factor);
+		ip = ROUND((DOUBLE)i/padding_factor);
+		jp = ROUND((DOUBLE)j/padding_factor);
 
 // TMP
 //#define CHECK_SIZE
@@ -519,13 +712,13 @@ void BackProjector::getDownsampledAverage(MultidimArray<Complex > &avg)
 
 void BackProjector::calculateDownSampledFourierShellCorrelation(MultidimArray<Complex > &avg1,
 																MultidimArray<Complex > &avg2,
-																MultidimArray<double> &fsc)
+																MultidimArray<DOUBLE> &fsc)
 {
 
     if (!avg1.sameShape(avg2))
     	REPORT_ERROR("ERROR BackProjector::calculateDownSampledFourierShellCorrelation: two arrays have different sizes");
 
-    MultidimArray<double> num, den1, den2;
+    MultidimArray<DOUBLE> num, den1, den2;
     num.initZeros(ori_size/2 + 1);
     den1.initZeros(num);
     den2.initZeros(num);
@@ -533,14 +726,14 @@ void BackProjector::calculateDownSampledFourierShellCorrelation(MultidimArray<Co
 
     FOR_ALL_ELEMENTS_IN_ARRAY3D(avg1)
     {
-    	double R = sqrt(k*k + i*i + j*j);
+    	DOUBLE R = sqrt(k*k + i*i + j*j);
         if (R > r_max)
             continue;
         int idx=ROUND(R);
         Complex z1=A3D_ELEM(avg1, k, i, j);
         Complex z2=A3D_ELEM(avg2, k, i, j);
-        double absz1=abs(z1);
-        double absz2=abs(z2);
+        DOUBLE absz1=abs(z1);
+        DOUBLE absz2=abs(z2);
         num(idx)+=(conj(z1) * z2).real;
         den1(idx)+= absz1*absz1;
         den2(idx)+= absz2*absz2;
@@ -560,15 +753,15 @@ void BackProjector::calculateDownSampledFourierShellCorrelation(MultidimArray<Co
 }
 
 
-void BackProjector::reconstruct(MultidimArray<double> &vol_out,
+void BackProjector::reconstruct(MultidimArray<DOUBLE> &vol_out,
                                 int max_iter_preweight,
                                 bool do_map,
-                                double tau2_fudge,
-                                MultidimArray<double> &tau2,
-                                MultidimArray<double> &sigma2,
-                                MultidimArray<double> &data_vs_prior,
-                                MultidimArray<double> fsc, // only input
-                                double normalise,
+                                DOUBLE tau2_fudge,
+                                MultidimArray<DOUBLE> &tau2,
+                                MultidimArray<DOUBLE> &sigma2,
+                                MultidimArray<DOUBLE> &data_vs_prior,
+                                MultidimArray<DOUBLE> fsc, // only input
+                                DOUBLE normalise,
                                 bool update_tau2_with_fsc,
                                 bool is_whole_instead_of_half,
                                 int nr_threads,
@@ -582,12 +775,15 @@ void BackProjector::reconstruct(MultidimArray<double> &vol_out,
     // Somehow I get lots of bad/non-reproducible errors when having these...
     //transformer.setThreadsNumber(nr_threads);
 	MultidimArray<Complex > Fconv;
-	MultidimArray<double> Fweight, Fnewweight;
+	MultidimArray<DOUBLE> Fweight;
+        // Fnewweight can become too large for a float: always keep this one in double-precision
+        MultidimArray<double> Fnewweight;
+
 	int max_r2 = r_max * r_max * padding_factor * padding_factor;
 
 //#define DEBUG_RECONSTRUCT
 #ifdef DEBUG_RECONSTRUCT
-	Image<double> ttt;
+	Image<DOUBLE> ttt;
 	FileName fnttt;
 	ttt()=weight;
 	ttt.write("reconstruct_initial_weight.spi");
@@ -636,8 +832,8 @@ void BackProjector::reconstruct(MultidimArray<double> &vol_out,
 	decenter(weight, Fweight, max_r2);
 
 	// Take oversampling into account
-	double oversampling_correction = (ref_dim == 3) ? (padding_factor * padding_factor * padding_factor) : (padding_factor * padding_factor);
-	MultidimArray<double> counter;
+	DOUBLE oversampling_correction = (ref_dim == 3) ? (padding_factor * padding_factor * padding_factor) : (padding_factor * padding_factor);
+	MultidimArray<DOUBLE> counter;
 
 
 	// First calculate the radial average of the (inverse of the) power of the noise in the reconstruction
@@ -651,8 +847,8 @@ void BackProjector::reconstruct(MultidimArray<double> &vol_out,
 		int r2 = kp * kp + ip * ip + jp * jp;
 		if (r2 < max_r2)
 		{
-			int ires = ROUND( sqrt((double)r2) / padding_factor );
-			double invw = oversampling_correction * DIRECT_A3D_ELEM(Fweight, k, i, j);
+			int ires = ROUND( sqrt((DOUBLE)r2) / padding_factor );
+			DOUBLE invw = oversampling_correction * DIRECT_A3D_ELEM(Fweight, k, i, j);
 			DIRECT_A1D_ELEM(sigma2, ires) += invw;
 			DIRECT_A1D_ELEM(counter, ires) += 1.;
 		}
@@ -687,7 +883,7 @@ void BackProjector::reconstruct(MultidimArray<double> &vol_out,
 		FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY1D(sigma2)
 		{
 			// FSC cannot be negative or zero for conversion into tau2
-			double myfsc = XMIPP_MAX(0.001, DIRECT_A1D_ELEM(fsc, i));
+			DOUBLE myfsc = XMIPP_MAX(0.001, DIRECT_A1D_ELEM(fsc, i));
 			if (is_whole_instead_of_half)
 			{
 				// Factor two because of twice as many particles
@@ -695,8 +891,8 @@ void BackProjector::reconstruct(MultidimArray<double> &vol_out,
 				myfsc = sqrt(2. * myfsc / (myfsc + 1.));
 			}
 			myfsc = XMIPP_MIN(0.999, myfsc);
-			double myssnr = myfsc / (1. - myfsc);
-			double fsc_based_tau = myssnr * DIRECT_A1D_ELEM(sigma2, i);
+			DOUBLE myssnr = myfsc / (1. - myfsc);
+			DOUBLE fsc_based_tau = myssnr * DIRECT_A1D_ELEM(sigma2, i);
 			DIRECT_A1D_ELEM(tau2, i) = fsc_based_tau;
 			// data_vs_prior is merely for reporting: it is not used for anything in the reconstruction
 			DIRECT_A1D_ELEM(data_vs_prior, i) = myssnr;
@@ -718,10 +914,10 @@ void BackProjector::reconstruct(MultidimArray<double> &vol_out,
 			int r2 = kp * kp + ip * ip + jp * jp;
 			if (r2 < max_r2)
 			{
-				int ires = ROUND( sqrt((double)r2) / padding_factor );
-				double invw = DIRECT_A3D_ELEM(Fweight, k, i, j);
+				int ires = ROUND( sqrt((DOUBLE)r2) / padding_factor );
+				DOUBLE invw = DIRECT_A3D_ELEM(Fweight, k, i, j);
 
-				double invtau2;
+				DOUBLE invtau2;
 				if (DIRECT_A1D_ELEM(tau2, ires) > 0.)
 				{
 					// Calculate inverse of tau2
@@ -811,15 +1007,15 @@ void BackProjector::reconstruct(MultidimArray<double> &vol_out,
 			DIRECT_MULTIDIM_ELEM(Fconv, n) = DIRECT_MULTIDIM_ELEM(Fnewweight, n) * DIRECT_MULTIDIM_ELEM(Fweight, n);
 		}
 
-        // convolute through Fourier-transform (as both grids are rectangular)
-        // Note that convoluteRealSpace acts on the complex array inside the transformer
-        convoluteBlobRealSpace(transformer);
+                // convolute through Fourier-transform (as both grids are rectangular)
+                // Note that convoluteRealSpace acts on the complex array inside the transformer
+                convoluteBlobRealSpace(transformer);
 
-        double w, corr_min = 99.e99, corr_max = -99.e99, corr_avg=0., corr_nn=0.;
-        FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(Fconv)
-        {
-        	if (kp * kp + ip * ip + jp * jp < max_r2)
-        	{
+                DOUBLE w, corr_min = 99.e99, corr_max = -99.e99, corr_avg=0., corr_nn=0.;
+                FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(Fconv)
+                {
+                    if (kp * kp + ip * ip + jp * jp < max_r2)
+                    {
 
         		// Make sure no division by zero can occur....
         		w = XMIPP_MAX(1e-6, abs(DIRECT_A3D_ELEM(Fconv, k, i, j)));
@@ -830,8 +1026,8 @@ void BackProjector::reconstruct(MultidimArray<double> &vol_out,
         		corr_nn += 1.;
         		// Apply division of Eq. [14] in Pipe & Menon (1999)
         		DIRECT_A3D_ELEM(Fnewweight, k, i, j) /= w;
-        	}
-        }
+                    }
+                }
 
 #ifdef DEBUG_RECONSTRUCT
         std::cerr << " PREWEIGHTING ITERATION: "<< iter + 1 << " OF " << max_iter_preweight << std::endl;
@@ -843,8 +1039,9 @@ void BackProjector::reconstruct(MultidimArray<double> &vol_out,
 	}
 
 #ifdef DEBUG_RECONSTRUCT
-	ttt()=Fnewweight;
-	ttt.write("reconstruct_gridding_weight.spi");
+	Image<double> tttt;
+	tttt()=Fnewweight;
+	tttt.write("reconstruct_gridding_weight.spi");
 	FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Fconv)
 	{
 		DIRECT_MULTIDIM_ELEM(ttt(), n) = abs(DIRECT_MULTIDIM_ELEM(Fconv, n));
@@ -863,6 +1060,11 @@ void BackProjector::reconstruct(MultidimArray<double> &vol_out,
 	decenter(data, Fconv, max_r2);
 	FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Fconv)
 	{
+#ifdef  FLOAT_PRECISION
+            // Prevent numerical instabilities in single-precision reconstruction with very unevenly sampled orientations
+            if (DIRECT_MULTIDIM_ELEM(Fnewweight, n) > 1e20)
+                DIRECT_MULTIDIM_ELEM(Fnewweight, n) = 1e20;
+#endif
 		DIRECT_MULTIDIM_ELEM(Fconv, n) *= DIRECT_MULTIDIM_ELEM(Fnewweight, n);
 	}
 
@@ -917,12 +1119,12 @@ void BackProjector::reconstruct(MultidimArray<double> &vol_out,
 	softMaskOutsideMap(vol_out);
 
 	// Gridding correction for the blob
-	double normftblob = tab_ftblob(0.);
+	DOUBLE normftblob = tab_ftblob(0.);
 	FOR_ALL_ELEMENTS_IN_ARRAY3D(vol_out)
 	{
 
-		double r = sqrt((double)(k*k+i*i+j*j));
-		double rval = r / (ori_size * padding_factor);
+		DOUBLE r = sqrt((DOUBLE)(k*k+i*i+j*j));
+		DOUBLE rval = r / (ori_size * padding_factor);
 		A3D_ELEM(vol_out, k, i, j) /= tab_ftblob(rval) / normftblob;
 		//if (k==0 && i==0)
 		//	std::cerr << " j= " << j << " rval= " << rval << " tab_ftblob(rval) / normftblob= " << tab_ftblob(rval) / normftblob << std::endl;
@@ -937,9 +1139,9 @@ void BackProjector::reconstruct(MultidimArray<double> &vol_out,
 	// This is the same as convolution with a SINC. It seems to give better maps.
 	// Then just make the blob look as much as a SINC as possible....
 	// The "standard" r1.9, m2 and a15 blob looks quite like a sinc until the first zero (perhaps that's why it is standard?)
-	//for (double r = 0.1; r < 10.; r+=0.01)
+	//for (DOUBLE r = 0.1; r < 10.; r+=0.01)
 	//{
-	//	double sinc = sin(PI * r / padding_factor ) / ( PI * r / padding_factor);
+	//	DOUBLE sinc = sin(PI * r / padding_factor ) / ( PI * r / padding_factor);
 	//	std::cout << " r= " << r << " sinc= " << sinc << " blob= " << blob_val(r, blob) << std::endl;
 	//}
 
@@ -964,13 +1166,13 @@ void BackProjector::reconstruct(MultidimArray<double> &vol_out,
 	{
 
 		// New tau2 will be the power spectrum of the new map
-		MultidimArray<double> spectrum, count;
+		MultidimArray<DOUBLE> spectrum, count;
 
 		// Calculate this map's power spectrum
 		// Don't call getSpectrum() because we want to use the same transformer object to prevent memory trouble....
 		spectrum.initZeros(XSIZE(vol_out));
 	    count.initZeros(XSIZE(vol_out));
-	    // recycle the same transformer for all imagses
+	    // recycle the same transformer for all images
 	    transformer.FourierTransform(vol_out, Fconv, false);
 	    FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(Fconv)
 	    {
@@ -982,7 +1184,7 @@ void BackProjector::reconstruct(MultidimArray<double> &vol_out,
 
 		// Factor two because of two-dimensionality of the complex plane
 		// (just like sigma2_noise estimates, the power spectra should be divided by 2)
-		double normfft = (ref_dim==3) ? (double)(ori_size * ori_size) : 1.;
+		DOUBLE normfft = (ref_dim == 3 && data_dim == 2) ? (DOUBLE)(ori_size * ori_size) : 1.;
 		spectrum *= normfft / 2.;
 
 		// New SNR^MAP will be power spectrum divided by the noise in the reconstruction (i.e. sigma2)
@@ -1003,7 +1205,7 @@ void BackProjector::reconstruct(MultidimArray<double> &vol_out,
 }
 
 void BackProjector::enforceHermitianSymmetry(MultidimArray<Complex > &my_data,
-											 MultidimArray<double> &my_weight)
+											 MultidimArray<DOUBLE> &my_weight)
 {
 
 	for (int iz = STARTINGZ(my_data); iz <=FINISHINGZ(my_data); iz++)
@@ -1016,7 +1218,7 @@ void BackProjector::enforceHermitianSymmetry(MultidimArray<Complex > &my_data,
 			Complex fsum = (A3D_ELEM(my_data, iz, iy, 0) + conj(A3D_ELEM(my_data, -iz, -iy, 0)));
 			A3D_ELEM(my_data, iz, iy, 0) = fsum;
 			A3D_ELEM(my_data, -iz, -iy, 0) = conj(fsum);
-			double sum = (A3D_ELEM(my_weight, iz, iy, 0) + A3D_ELEM(my_weight, -iz, -iy, 0));
+			DOUBLE sum = (A3D_ELEM(my_weight, iz, iy, 0) + A3D_ELEM(my_weight, -iz, -iy, 0));
 			A3D_ELEM(my_weight, iz, iy, 0) = sum;
 			A3D_ELEM(my_weight, -iz, -iy, 0) = sum;
 		}
@@ -1025,7 +1227,7 @@ void BackProjector::enforceHermitianSymmetry(MultidimArray<Complex > &my_data,
 }
 
 void BackProjector::symmetrise(MultidimArray<Complex > &my_data,
-		 MultidimArray<double> &my_weight, int my_rmax2)
+		 MultidimArray<DOUBLE> &my_weight, int my_rmax2)
 {
 
 //#define DEBUG_SYMM
@@ -1036,16 +1238,16 @@ void BackProjector::symmetrise(MultidimArray<Complex > &my_data,
 
 	if (SL.SymsNo() > 0 && ref_dim == 3)
 	{
-		Matrix2D<double> L(4, 4), R(4, 4); // A matrix from the list
-		MultidimArray<double> sum_weight;
+		Matrix2D<DOUBLE> L(4, 4), R(4, 4); // A matrix from the list
+		MultidimArray<DOUBLE> sum_weight;
 		MultidimArray<Complex > sum_data;
-        double x, y, z, fx, fy, fz, xp, yp, zp, r2;
+        DOUBLE x, y, z, fx, fy, fz, xp, yp, zp, r2;
         bool is_neg_x;
         int x0, x1, y0, y1, z0, z1;
     	Complex d000, d001, d010, d011, d100, d101, d110, d111;
     	Complex dx00, dx01, dx10, dx11, dxy0, dxy1;
-    	double dd000, dd001, dd010, dd011, dd100, dd101, dd110, dd111;
-    	double ddx00, ddx01, ddx10, ddx11, ddxy0, ddxy1;
+    	DOUBLE dd000, dd001, dd010, dd011, dd100, dd101, dd110, dd111;
+    	DOUBLE ddx00, ddx01, ddx10, ddx11, ddxy0, ddxy1;
 
         // First symmetry operator (not stored in SL) is the identity matrix
 		sum_weight = my_weight;
@@ -1062,9 +1264,9 @@ void BackProjector::symmetrise(MultidimArray<Complex > &my_data,
 	        FOR_ALL_ELEMENTS_IN_ARRAY3D(sum_weight)
 	        {
 
-	        	x = (double)j; // STARTINGX(sum_weight) is zero!
-	        	y = (double)i;
-	        	z = (double)k;
+	        	x = (DOUBLE)j; // STARTINGX(sum_weight) is zero!
+	        	y = (DOUBLE)i;
+	        	z = (DOUBLE)k;
 	        	r2 = x*x + y*y + z*z;
 	        	if (r2 <= my_rmax2)
 	        	{
@@ -1171,8 +1373,8 @@ void BackProjector::symmetrise(MultidimArray<Complex > &my_data,
 	    /*
 	    FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(data)
 	    {
-	    	DIRECT_MULTIDIM_ELEM(data, n) = DIRECT_MULTIDIM_ELEM(sum_data, n) / (double)(SL.SymsNo() + 1);
-	    	DIRECT_MULTIDIM_ELEM(weight, n) = DIRECT_MULTIDIM_ELEM(sum_weight, n) / (double)(SL.SymsNo() + 1);
+	    	DIRECT_MULTIDIM_ELEM(data, n) = DIRECT_MULTIDIM_ELEM(sum_data, n) / (DOUBLE)(SL.SymsNo() + 1);
+	    	DIRECT_MULTIDIM_ELEM(weight, n) = DIRECT_MULTIDIM_ELEM(sum_weight, n) / (DOUBLE)(SL.SymsNo() + 1);
 	    }
 	    */
 	}
@@ -1182,7 +1384,7 @@ void BackProjector::symmetrise(MultidimArray<Complex > &my_data,
 void BackProjector::convoluteBlobRealSpace(FourierTransformer &transformer, bool do_mask)
 {
 
-	MultidimArray<double> Mconv;
+	MultidimArray<DOUBLE> Mconv;
 	int padhdim = pad_size / 2;
 
 	// Set up right dimension of real-space array
@@ -1197,7 +1399,7 @@ void BackProjector::convoluteBlobRealSpace(FourierTransformer &transformer, bool
 	transformer.inverseFourierTransform();
 
 	// Blob normalisation in Fourier space
-	double normftblob = tab_ftblob(0.);
+	DOUBLE normftblob = tab_ftblob(0.);
 
 	// TMP DEBUGGING
 	//struct blobtype blob;
@@ -1211,7 +1413,7 @@ void BackProjector::convoluteBlobRealSpace(FourierTransformer &transformer, bool
 		int kp = (k < padhdim) ? k : k - pad_size;
 		int ip = (i < padhdim) ? i : i - pad_size;
 		int jp = (j < padhdim) ? j : j - pad_size;
-    	double rval = sqrt ( (double)(kp * kp + ip * ip + jp * jp) ) / (ori_size * padding_factor);
+    	DOUBLE rval = sqrt ( (DOUBLE)(kp * kp + ip * ip + jp * jp) ) / (ori_size * padding_factor);
     	//if (kp==0 && ip==0 && jp > 0)
 		//	std::cerr << " jp= " << jp << " rval= " << rval << " tab_ftblob(rval) / normftblob= " << tab_ftblob(rval) / normftblob << " ori_size/2= " << ori_size/2 << std::endl;
     	// In the final reconstruction: mask the real-space map beyond its original size to prevent aliasing ghosts
@@ -1227,16 +1429,16 @@ void BackProjector::convoluteBlobRealSpace(FourierTransformer &transformer, bool
 
 }
 
-void BackProjector::windowToOridimRealSpace(FourierTransformer &transformer, MultidimArray<Complex > &Fin, MultidimArray<double> &Mout, int nr_threads)
+void BackProjector::windowToOridimRealSpace(FourierTransformer &transformer, MultidimArray<Complex > &Fin, MultidimArray<DOUBLE> &Mout, int nr_threads)
 {
 
 	MultidimArray<Complex > Ftmp;
 	int padoridim = padding_factor * ori_size;
-	double normfft;
+	DOUBLE normfft;
 
 //#define DEBUG_WINDOWORIDIMREALSPACE
 #ifdef DEBUG_WINDOWORIDIMREALSPACE
-	Image<double> tt;
+	Image<DOUBLE> tt;
 	tt().resize(ZSIZE(Fin), YSIZE(Fin), XSIZE(Fin));
 	FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Fin)
 	{
@@ -1248,12 +1450,15 @@ void BackProjector::windowToOridimRealSpace(FourierTransformer &transformer, Mul
 	if (ref_dim == 2)
 	{
 		Mout.resize(padoridim, padoridim);
-		normfft = (double)(padding_factor * padding_factor);
+		normfft = (DOUBLE)(padding_factor * padding_factor);
 	}
 	else
 	{
 		Mout.resize(padoridim, padoridim, padoridim);
-		normfft = (double)(padding_factor * padding_factor * padding_factor * ori_size);
+		if (data_dim == 3)
+			normfft = (DOUBLE)(padding_factor * padding_factor * padding_factor);
+		else
+			normfft = (DOUBLE)(padding_factor * padding_factor * padding_factor * ori_size);
 	}
 	Mout.setXmippOrigin();
 
diff --git a/src/backprojector.h b/src/backprojector.h
index 3a01093..bc2aa03 100644
--- a/src/backprojector.h
+++ b/src/backprojector.h
@@ -33,11 +33,12 @@
 #include "src/tabfuncs.h"
 #include "src/symmetries.h"
 
+
 class BackProjector: public Projector
 {
 public:
 	// For backward projection: sum of weights
-	MultidimArray<double> weight;
+	MultidimArray<DOUBLE> weight;
 
 	// Tabulated blob values
 	TabFtBlob tab_ftblob;
@@ -57,7 +58,7 @@ public:
 	 */
 	BackProjector(int _ori_size, int _ref_dim, FileName fn_sym,
 			      int _interpolator = TRILINEAR, int _padding_factor_3d = 2, int _r_min_nn = 10,
-			      int _blob_order = 0, double _blob_radius = 1.9, double _blob_alpha = 15)
+			      int _blob_order = 0, DOUBLE _blob_radius = 1.9, DOUBLE _blob_alpha = 15, int _data_dim = 2)
 	{
     	// Store original dimension
     	ori_size = _ori_size;
@@ -65,6 +66,9 @@ public:
     	// Set dimensionality of the references
     	ref_dim = _ref_dim;
 
+    	// and of the data
+    	data_dim = _data_dim;
+
     	// Set the symmetry object
     	SL.read_sym_file(fn_sym);
 
@@ -115,6 +119,7 @@ public:
         	interpolator = op.interpolator;
         	padding_factor = op.padding_factor;
         	ref_dim = op.ref_dim;
+        	data_dim = op.data_dim;
          	// BackProjector stuff
         	weight = op.weight;
         	tab_ftblob = op.tab_ftblob;
@@ -153,20 +158,29 @@ public:
 	* Depending on the dimension of the map, this will be a backprojection or a rotation operation
 	*/
 	void set2DFourierTransform(const MultidimArray<Complex > &img_in,
-							   const Matrix2D<double> &A, bool inv,
-						       const MultidimArray<double> *Mweight = NULL)
+							   const Matrix2D<DOUBLE> &A, bool inv,
+						       const MultidimArray<DOUBLE> *Mweight = NULL)
 	{
 		// Back-rotation of a 3D Fourier Transform
-		switch (ref_dim)
+		if (img_in.getDim() == 3)
+		{
+			if (ref_dim != 3)
+				REPORT_ERROR("Backprojector::set3DFourierTransform%%ERROR: Dimension of the data array should be 3");
+			backrotate3D(img_in, A, inv, Mweight);
+		}
+		else
 		{
-		case 2:
-			backrotate2D(img_in, A, inv, Mweight);
-			break;
-		case 3:
-			backproject(img_in, A, inv, Mweight);
-			break;
-		default:
-			REPORT_ERROR("Backprojector::set2DSlice%%ERROR: Dimension of the data array should be 2 or 3");
+			switch (ref_dim)
+			{
+			case 2:
+				backrotate2D(img_in, A, inv, Mweight);
+				break;
+			case 3:
+				backproject(img_in, A, inv, Mweight);
+				break;
+			default:
+				REPORT_ERROR("Backprojector::set2DSlice%%ERROR: Dimension of the data array should be 2 or 3");
+			}
 		}
 	}
 
@@ -175,29 +189,37 @@ public:
 	* If a exp_Mweight is given, rather than adding 1 to all relevant pixels in the weight array, we use exp_Mweight
 	*/
 	void backrotate2D(const MultidimArray<Complex > &img_in,
-			          const Matrix2D<double> &A, bool inv,
-			          const MultidimArray<double> *Mweight = NULL);
+			          const Matrix2D<DOUBLE> &A, bool inv,
+			          const MultidimArray<DOUBLE> *Mweight = NULL);
+
+	/*
+	* Set a 3D-rotated version of the 3D map into the data array (mere interpolation)
+	* If a exp_Mweight is given, rather than adding 1 to all relevant pixels in the weight array, we use exp_Mweight
+	*/
+	void backrotate3D(const MultidimArray<Complex > &img_in,
+			          const Matrix2D<DOUBLE> &A, bool inv,
+			          const MultidimArray<DOUBLE> *Mweight = NULL);
 
 	/*
 	* Set a 2D slice in the 3D map (backward projection)
 	* If a exp_Mweight is given, rather than adding 1 to all relevant pixels in the weight array, we use exp_Mweight
 	*/
 	void backproject(const MultidimArray<Complex > &img_in,
-			         const Matrix2D<double> &A, bool inv,
-			         const MultidimArray<double> *Mweight = NULL);
+			         const Matrix2D<DOUBLE> &A, bool inv,
+			         const MultidimArray<DOUBLE> *Mweight = NULL);
 
 	/*
 	 * Get only the lowest resolution components from the data and weight array
 	 * (to be joined together for two independent halves in order to force convergence in the same orientation)
 	 */
-	void getLowResDataAndWeight(MultidimArray<Complex > &lowres_data, MultidimArray<double> &lowres_weight,
+	void getLowResDataAndWeight(MultidimArray<Complex > &lowres_data, MultidimArray<DOUBLE> &lowres_weight,
 			int lowres_r_max);
 
 	/*
 	 * Set only the lowest resolution components from the data and weight array
 	 * (to be joined together for two independent halves in order to force convergence in the same orientation)
 	 */
-	void setLowResDataAndWeight(MultidimArray<Complex > &lowres_data, MultidimArray<double> &lowres_weight,
+	void setLowResDataAndWeight(MultidimArray<Complex > &lowres_data, MultidimArray<DOUBLE> &lowres_weight,
 			int lowres_r_max);
 
 	/*
@@ -212,21 +234,21 @@ public:
 	 */
 	void calculateDownSampledFourierShellCorrelation(MultidimArray<Complex > &avg1,
 			                                         MultidimArray<Complex > &avg2,
-			                                         MultidimArray<double> &fsc);
+			                                         MultidimArray<DOUBLE> &fsc);
 
 	/* Get the 3D reconstruction
          * If do_map is true, 1 will be added to all weights
          * alpha will contain the noise-reduction spectrum
 	*/
-	void reconstruct(MultidimArray<double> &vol_out,
+	void reconstruct(MultidimArray<DOUBLE> &vol_out,
                      int max_iter_preweight,
                      bool do_map,
-                     double tau2_fudge,
-                     MultidimArray<double> &tau2,
-                     MultidimArray<double> &sigma2,
-                     MultidimArray<double> &evidence_vs_prior,
-                     MultidimArray<double> fsc,
-                     double normalise = 1.,
+                     DOUBLE tau2_fudge,
+                     MultidimArray<DOUBLE> &tau2,
+                     MultidimArray<DOUBLE> &sigma2,
+                     MultidimArray<DOUBLE> &evidence_vs_prior,
+                     MultidimArray<DOUBLE> fsc,
+                     DOUBLE normalise = 1.,
                      bool update_tau2_with_fsc = false,
                      bool is_whole_instead_of_half = false,
                      int nr_threads = 1,
@@ -237,12 +259,12 @@ public:
 	* Repairing it here gives like a 2-fold averaging correction for interpolation errors...
     */
 	void enforceHermitianSymmetry(MultidimArray<Complex > &mydata,
-								  MultidimArray<double> &myweight);
+								  MultidimArray<DOUBLE> &myweight);
 
 	/* Applies the symmetry from the SymList object to the weight and the data array
 	 */
 	void symmetrise(MultidimArray<Complex > &mydata,
-					MultidimArray<double> &myweight, int my_rmax2);
+					MultidimArray<DOUBLE> &myweight, int my_rmax2);
 
    /* Convolute in Fourier-space with the blob by multiplication in real-space
 	 * Note the convlution is done on the complex array inside the transformer object!!
@@ -252,7 +274,7 @@ public:
 	/* Calculate the inverse FFT of Fin and windows the result to ori_size
 	 * Also pass the transformer, to prevent making and clearing a new one before clearing the one in reconstruct()
 	 */
-	void windowToOridimRealSpace(FourierTransformer &transformer, MultidimArray<Complex > &Fin, MultidimArray<double> &Mout, int nr_threads = 1);
+	void windowToOridimRealSpace(FourierTransformer &transformer, MultidimArray<Complex > &Fin, MultidimArray<DOUBLE> &Mout, int nr_threads = 1);
 
    /*
 	* Go from the Projector-centered fourier transform back to FFTW-uncentered one
@@ -271,6 +293,24 @@ public:
 	   }
    }
 
+
+#ifdef FLOAT_PRECISION
+   // Fnewweight needs decentering, but has to be in double-precision for correct calculations!
+   template <typename T>
+   void decenter(MultidimArray<T> &Min, MultidimArray<double> &Mout, int my_rmax2)
+   {
+
+	   // Mout should already have the right size
+	   // Initialize to zero
+	   Mout.initZeros();
+	   FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(Mout)
+	   {
+		   if (kp*kp + ip*ip + jp*jp <= my_rmax2)
+                       DIRECT_A3D_ELEM(Mout, k, i, j) = (double)A3D_ELEM(Min, kp, ip, jp);
+	   }
+   }
+#endif
+
 };
 
 #endif /* BACKPROJECTOR_H_ */
diff --git a/src/complex.cpp b/src/complex.cpp
index 1421e89..eb65002 100644
--- a/src/complex.cpp
+++ b/src/complex.cpp
@@ -20,7 +20,7 @@
 #include "src/complex.h"
 
 // Constructor with two arguments
-Complex::Complex(double _r, double _i)
+Complex::Complex(DOUBLE _r, DOUBLE _i)
 {
     real = _r;
     imag = _i;
@@ -52,31 +52,31 @@ Complex Complex::operator* (Complex &op)
     return Complex((real * op.real) - (imag * op.imag), (real * op.imag) + (imag * op.real));
 }
 
-Complex Complex::operator* (double op)
+Complex Complex::operator* (DOUBLE op)
 {
     return Complex(real*op, imag*op);
 }
 
-void Complex::operator*= (double op)
+void Complex::operator*= (DOUBLE op)
 {
 	real *= op;
 	imag *= op;
 }
 
-Complex Complex::operator/(double op)
+Complex Complex::operator/(DOUBLE op)
 {
     return Complex(real/op, imag/op);
 }
 
 Complex Complex::operator/(Complex &op)
 {
-    double cd = op.norm();
-    double realval = real*op.real + imag*op.imag;
-    double imagval = imag*op.real - real*op.imag;
+    DOUBLE cd = op.norm();
+    DOUBLE realval = real*op.real + imag*op.imag;
+    DOUBLE imagval = imag*op.real - real*op.imag;
 	return Complex(realval/cd, imagval/cd);
 }
 
-void Complex::operator/=(double op)
+void Complex::operator/=(DOUBLE op)
 {
 	real /= op;
 	imag /= op;
@@ -99,17 +99,17 @@ Complex operator*(const Complex& lhs, const Complex& rhs)
 	return Complex((lhs.real * rhs.real) - (lhs.imag * rhs.imag), (lhs.real * rhs.imag) + (lhs.imag * rhs.real));
 }
 
-Complex operator*(const Complex& lhs, const double& val)
+Complex operator*(const Complex& lhs, const DOUBLE& val)
 {
 	return Complex(lhs.real * val , lhs.imag * val);
 }
 
-Complex operator*(const double& val, const Complex& rhs)
+Complex operator*(const DOUBLE& val, const Complex& rhs)
 {
 	return Complex(rhs.real * val , rhs.imag * val);
 }
 
-Complex operator/(const Complex& lhs, const double& val)
+Complex operator/(const Complex& lhs, const DOUBLE& val)
 {
 	return Complex(lhs.real / val , lhs.imag / val);
 }
@@ -135,30 +135,30 @@ Complex conj(const Complex& op)
 }
 
 
-double Complex::abs()
+DOUBLE Complex::abs()
 {
     return sqrt(real*real + imag*imag);
 }
-double abs(const Complex& op)
+DOUBLE abs(const Complex& op)
 {
 	return sqrt(op.real*op.real + op.imag*op.imag);
 }
 
-double Complex::norm()
+DOUBLE Complex::norm()
 {
     return real*real + imag*imag;
 }
-double norm(const Complex& op)
+DOUBLE norm(const Complex& op)
 {
 	return op.real*op.real + op.imag*op.imag;
 }
 
-double Complex::arg()
+DOUBLE Complex::arg()
 {
     return atan2(imag, real);
 }
 
-double arg(const Complex& op)
+DOUBLE arg(const Complex& op)
 {
 	return atan2(op.imag, op.real);
 }
diff --git a/src/complex.h b/src/complex.h
index 2c2197e..8ced25b 100644
--- a/src/complex.h
+++ b/src/complex.h
@@ -21,17 +21,18 @@
 #define COMPLEX_H_
 #include <iostream>
 #include <cmath>
+#include "src/macros.h"
 
 class Complex
 {
 
 	public:
 
-	double real;
-	double imag;
+	DOUBLE real;
+	DOUBLE imag;
 
     // Constructor
-	Complex(double _r = 0.0, double _i = 0.0);
+	Complex(DOUBLE _r = 0.0, DOUBLE _i = 0.0);
 
     Complex operator+(Complex &op);
     void operator+=(Complex &op);
@@ -41,42 +42,42 @@ class Complex
 
     Complex operator*(Complex &op);
 
-    void operator*=(double op);
+    void operator*=(DOUBLE op);
 
-    Complex operator*(double op);
+    Complex operator*(DOUBLE op);
 
     Complex operator/(Complex &op);
 
-    Complex operator/(double op);
+    Complex operator/(DOUBLE op);
 
-    void operator/=(double op);
+    void operator/=(DOUBLE op);
 
     // Complex conjugated
     Complex conj();
 
     // Abs value: sqrt(real*real+imag*imag)
-    double abs();
+    DOUBLE abs();
 
     // Norm value: real*real+imag*imag
-    double norm();
+    DOUBLE norm();
 
     // Phase angle: atan2(imag,real)
-    double arg();
+    DOUBLE arg();
 
 
 };
 
 Complex conj(const Complex& op);
-double abs(const Complex& op);
-double norm(const Complex& op);
-double arg(const Complex& op);
+DOUBLE abs(const Complex& op);
+DOUBLE norm(const Complex& op);
+DOUBLE arg(const Complex& op);
 
 Complex operator+(const Complex& lhs, const Complex& rhs);
 Complex operator-(const Complex& lhs, const Complex& rhs);
 Complex operator*(const Complex& lhs, const Complex& rhs);
-Complex operator*(const Complex& lhs, const double& val);
-Complex operator*(const double& val, const Complex& rhs);
-Complex operator/(const Complex& lhs, const double& val);
+Complex operator*(const Complex& lhs, const DOUBLE& val);
+Complex operator*(const DOUBLE& val, const Complex& rhs);
+Complex operator/(const Complex& lhs, const DOUBLE& val);
 
 void operator+=(Complex& lhs, const Complex& rhs);
 void operator-=(Complex& lhs, const Complex& rhs);
diff --git a/src/ctf.cpp b/src/ctf.cpp
index 0f3bb68..44754a6 100644
--- a/src/ctf.cpp
+++ b/src/ctf.cpp
@@ -85,8 +85,8 @@ void CTF::read(MetaDataTable &MD1, MetaDataTable &MD2, long int objectID)
 
 	initialise();
 }
-void CTF::setValues(double _defU, double _defV, double _defAng, double _voltage,
-		double _Cs, double _Q0, double _Bfac, double _scale)
+void CTF::setValues(DOUBLE _defU, DOUBLE _defV, DOUBLE _defAng, DOUBLE _voltage,
+		DOUBLE _Cs, DOUBLE _Q0, DOUBLE _Bfac, DOUBLE _scale)
 {
 	kV              = _voltage;
 	DeltafU         = _defU;
@@ -138,8 +138,8 @@ void CTF::initialise()
 {
 
 	// Change units
-    double local_Cs = Cs * 1e7;
-    double local_kV = kV * 1e3;
+    DOUBLE local_Cs = Cs * 1e7;
+    DOUBLE local_kV = kV * 1e3;
     rad_azimuth = DEG2RAD(azimuthal_angle);
 
     // Average focus and deviation
@@ -171,31 +171,31 @@ void CTF::initialise()
 }
 
 /* Generate a complete CTF Image ------------------------------------------------------ */
-void CTF::getFftwImage(MultidimArray<double> &result, int orixdim, int oriydim, double angpix,
+void CTF::getFftwImage(MultidimArray<DOUBLE> &result, int orixdim, int oriydim, DOUBLE angpix,
 		    		bool do_abs, bool do_only_flip_phases, bool do_intact_until_first_peak, bool do_damping)
 {
 
-	double xs = (double)orixdim * angpix;
-	double ys = (double)oriydim * angpix;
+	DOUBLE xs = (DOUBLE)orixdim * angpix;
+	DOUBLE ys = (DOUBLE)oriydim * angpix;
 	FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM2D(result)
 	{
-		double x = (double)jp / xs;
-		double y = (double)ip / ys;
+		DOUBLE x = (DOUBLE)jp / xs;
+		DOUBLE y = (DOUBLE)ip / ys;
 		DIRECT_A2D_ELEM(result, i, j) = getCTF(x, y, do_abs, do_only_flip_phases, do_intact_until_first_peak, do_damping);
 	}
 }
 
-void CTF::getCenteredImage(MultidimArray<double> &result, double Tm,
+void CTF::getCenteredImage(MultidimArray<DOUBLE> &result, DOUBLE Tm,
 		    		bool do_abs, bool do_only_flip_phases, bool do_intact_until_first_peak, bool do_damping)
 {
 	result.setXmippOrigin();
-	double xs = (double)XSIZE(result) * Tm;
-	double ys = (double)YSIZE(result) * Tm;
+	DOUBLE xs = (DOUBLE)XSIZE(result) * Tm;
+	DOUBLE ys = (DOUBLE)YSIZE(result) * Tm;
 
 	FOR_ALL_ELEMENTS_IN_ARRAY2D(result)
 	{
-		double x = (double)j / xs;
-		double y = (double)i / ys;
+		DOUBLE x = (DOUBLE)j / xs;
+		DOUBLE y = (DOUBLE)i / ys;
 		A2D_ELEM(result, i, j) = getCTF(x, y, do_abs, do_only_flip_phases, do_intact_until_first_peak, do_damping);
 	}
 
diff --git a/src/ctf.h b/src/ctf.h
index ce566df..d3066fb 100644
--- a/src/ctf.h
+++ b/src/ctf.h
@@ -54,69 +54,69 @@ class CTF
 protected:
 
     // Different constants
-    double K1;
-    double K2;
-    double K3;
-    double K4;
+    DOUBLE K1;
+    DOUBLE K2;
+    DOUBLE K3;
+    DOUBLE K4;
 
     // Azimuthal angle in radians
-    double rad_azimuth;
+    DOUBLE rad_azimuth;
 
     // defocus_average = (defocus_u + defocus_v)/2
-    double defocus_average;
+    DOUBLE defocus_average;
 
     // defocus_deviation = (defocus_u - defocus_v)/2
-    double defocus_deviation;
+    DOUBLE defocus_deviation;
 
 public:
 
     /// Accelerating Voltage (in KiloVolts)
-    double kV;
+    DOUBLE kV;
 
     /// Defocus in U (in Angstroms). Positive values are underfocused
-    double DeltafU;
+    DOUBLE DeltafU;
 
     /// Defocus in V (in Angstroms). Postive values are underfocused
-    double DeltafV;
+    DOUBLE DeltafV;
 
     /// Azimuthal angle (between X and U) in degrees
-    double azimuthal_angle;
+    DOUBLE azimuthal_angle;
 
     // Electron wavelength (Amstrongs)
-    double lambda;
+    DOUBLE lambda;
 
     // Radius of the aperture (in micras)
-    // double aperture;
+    // DOUBLE aperture;
     /// Spherical aberration (in milimeters). Typical value 5.6
-    double Cs;
+    DOUBLE Cs;
 
     /// Chromatic aberration (in milimeters). Typical value 2
-    double Ca;
+    DOUBLE Ca;
 
     /** Mean energy loss (eV) due to interaction with sample.
         Typical value 1*/
-    double espr;
+    DOUBLE espr;
 
     /// Objective lens stability (deltaI/I) (ppm). Typical value 1
-    double ispr;
+    DOUBLE ispr;
 
     /// Convergence cone semiangle (in mrad). Typical value 0.5
-    double alpha;
+    DOUBLE alpha;
 
     /// Longitudinal mechanical displacement (Angstrom). Typical value 100
-    double DeltaF;
+    DOUBLE DeltaF;
 
     /// Transversal mechanical displacement (Angstrom). Typical value 3
-    double DeltaR;
+    DOUBLE DeltaR;
 
     /// Amplitude contrast. Typical values 0.07 for cryo, 0.2 for negative stain
-    double Q0;
+    DOUBLE Q0;
 
     // B-factor fall-off
-    double Bfac;
+    DOUBLE Bfac;
 
     // Overall scale-factor of CTF
-    double scale;
+    DOUBLE scale;
 
     /** Empty constructor. */
     CTF() { clear(); }
@@ -129,8 +129,8 @@ public:
     void read(MetaDataTable &MD1, MetaDataTable &MD2, long int objectID = -1);
 
     /** Just set all values explicitly */
-    void setValues(double _defU, double _defV, double _defAng,
-    		double _voltage, double _Cs, double _Q0, double _Bfac, double _scale = 1.);
+    void setValues(DOUBLE _defU, DOUBLE _defV, DOUBLE _defAng,
+    		DOUBLE _voltage, DOUBLE _Cs, DOUBLE _Q0, DOUBLE _Bfac, DOUBLE _scale = 1.);
 
     /** Read from a single MetaDataTable */
     void read(MetaDataTable &MD);
@@ -146,28 +146,28 @@ public:
     void initialise();
 
     /// Compute CTF at (U,V). Continuous frequencies
-    inline double getCTF(double X, double Y,
+    inline DOUBLE getCTF(DOUBLE X, DOUBLE Y,
     		bool do_abs = false, bool do_only_flip_phases = false, bool do_intact_until_first_peak = false, bool do_damping = true) const
     {
-        double u2 = X * X + Y * Y;
-        double u = sqrt(u2);
-        double u4 = u2 * u2;
+        DOUBLE u2 = X * X + Y * Y;
+        DOUBLE u = sqrt(u2);
+        DOUBLE u4 = u2 * u2;
         // if (u2>=ua2) return 0;
-        double deltaf = getDeltaF(X, Y);
-        double argument = K1 * deltaf * u2 + K2 * u4;
-        double retval;
+        DOUBLE deltaf = getDeltaF(X, Y);
+        DOUBLE argument = K1 * deltaf * u2 + K2 * u4;
+        DOUBLE retval;
         if (do_intact_until_first_peak && ABS(argument) < PI/2.)
         {
         	retval = 1.;
         }
         else
         {
-        	double sine_part,cosine_part;
+        	DOUBLE sine_part,cosine_part;
             retval = -(K3*sin(argument) - Q0*cos(argument)); // Q0 should be positive
         }
         if (do_damping)
         {
-        	double E = exp(K4 * u2); // B-factor decay (K4 = -Bfac/4);
+        	DOUBLE E = exp(K4 * u2); // B-factor decay (K4 = -Bfac/4);
         	retval *= E;
         }
         if (do_abs)
@@ -182,13 +182,13 @@ public:
     }
 
     /// Compute Deltaf at a given direction
-    inline double getDeltaF(double X, double Y) const
+    inline DOUBLE getDeltaF(DOUBLE X, DOUBLE Y) const
     {
         if (ABS(X) < XMIPP_EQUAL_ACCURACY &&
             ABS(Y) < XMIPP_EQUAL_ACCURACY)
             return 0;
 
-        double ellipsoid_ang = atan2(Y, X) - rad_azimuth;
+        DOUBLE ellipsoid_ang = atan2(Y, X) - rad_azimuth;
         /*
         * For a derivation of this formulae confer
         * Principles of Electron Optics page 1380
@@ -197,18 +197,18 @@ public:
         * of the zernike polynomials difference of defocus at 0
         * and at 45 degrees. In this case a2=0
         */
-        double cos_ellipsoid_ang_2 = cos(2*ellipsoid_ang);
+        DOUBLE cos_ellipsoid_ang_2 = cos(2*ellipsoid_ang);
         return (defocus_average + defocus_deviation*cos_ellipsoid_ang_2);
 
     }
 
     /// Generate (Fourier-space, i.e. FFTW format) image with all CTF values.
     /// The dimensions of the result array should have been set correctly already
-    void getFftwImage(MultidimArray < double > &result, int orixdim, int oriydim, double angpix,
+    void getFftwImage(MultidimArray < DOUBLE > &result, int orixdim, int oriydim, DOUBLE angpix,
     		bool do_abs = false, bool do_only_flip_phases = false, bool do_intact_until_first_peak = false, bool do_damping = true);
 
     /// Generate a centered image (with hermitian symmetry)
-    void getCenteredImage(MultidimArray < double > &result, double angpix,
+    void getCenteredImage(MultidimArray < DOUBLE > &result, DOUBLE angpix,
     		bool do_abs = false, bool do_only_flip_phases = false, bool do_intact_until_first_peak = false, bool do_damping = true);
 
 
diff --git a/src/ctffind_runner.cpp b/src/ctffind_runner.cpp
index 2639d83..8fdca18 100644
--- a/src/ctffind_runner.cpp
+++ b/src/ctffind_runner.cpp
@@ -27,13 +27,13 @@ void CtffindRunner::read(int argc, char **argv, int rank)
 	int ctf_section = parser.addSection("CTF estimation");
 	fn_in = parser.getOption("--i", "STAR file with all input micrographs, or a unix wildcard to all micrograph files, e.g. \"mics/*.mrc\"");
 	fn_out = parser.getOption("--o", "Name for the STAR file with CTF params for each micrograph", "micrographs_ctf.star");
-	do_only_join_results = parser.checkOption("--only_make_star", "Don't run CTFFIND3, only join the logfile results in a STAR file");
-	continue_old = parser.checkOption("--only_do_unfinished", "Only run CTFFIND3 for those micrographs for which there is not yet a logfile with Final values.");
+	do_only_join_results = parser.checkOption("--only_make_star", "Don't run CTFFIND, only join the logfile results in a STAR file");
+	continue_old = parser.checkOption("--only_do_unfinished", "Only run CTFFIND for those micrographs for which there is not yet a logfile with Final values.");
 
 	// Use a smaller squared part of the micrograph to estimate CTF (e.g. to avoid film labels...)
 	ctf_win =  textToInteger(parser.getOption("--ctfWin", "Size (in pixels) of a centered, squared window to use for CTF-estimation", "-1"));
 
-	fn_ctffind3_exe = parser.getOption("--ctffind3_exe","Location of ctffind3 executable (or through RLN_CTFFIND3_EXECUTABLE environment variable)","");
+	fn_ctffind_exe = parser.getOption("--ctffind_exe","Location of ctffind executable (or through RELION_CTFFIND_EXECUTABLE environment variable)","");
 
 	// First parameter line in CTFFIND
 	Cs = textToFloat(parser.getOption("--CS", "Spherical Aberration (mm) ","2.0"));
@@ -67,13 +67,13 @@ void CtffindRunner::usage()
 void CtffindRunner::initialise()
 {
 
-	// Get the CTFFIND3 executable
-	if (fn_ctffind3_exe == "")
+	// Get the CTFFIND executable
+	if (fn_ctffind_exe == "")
 	{
 		char * penv;
-		penv = getenv ("RLN_CTFFIND3_EXECUTABLE");
+		penv = getenv ("RELION_CTFFIND_EXECUTABLE");
 		if (penv!=NULL)
-			fn_ctffind3_exe = (std::string)penv;
+			fn_ctffind_exe = (std::string)penv;
 	}
 
 	// Set up which micrographs to estimate CTFs from
@@ -101,8 +101,8 @@ void CtffindRunner::initialise()
 		for (long int imic = 0; imic < fn_micrographs.size(); imic++)
 		{
 			FileName fn_microot = fn_micrographs[imic].without(".mrc");
-			double defU, defV, defAng, CC, HT, CS, AmpCnst, XMAG, DStep;
-			if (!getCtffind3Results(fn_microot, defU, defV, defAng, CC,
+			DOUBLE defU, defV, defAng, CC, HT, CS, AmpCnst, XMAG, DStep;
+			if (!getCtffindResults(fn_microot, defU, defV, defAng, CC,
 					HT, CS, AmpCnst, XMAG, DStep, false)) // false: dont die if not found Final values
 				fns_todo.push_back(fn_micrographs[imic]);
 		}
@@ -114,7 +114,7 @@ void CtffindRunner::initialise()
 
 	if (verb > 0)
 	{
-		std::cout << " Using CTFFINDs executable in: " << fn_ctffind3_exe << std::endl;
+		std::cout << " Using CTFFINDs executable in: " << fn_ctffind_exe << std::endl;
 		std::cout << " to estimate CTF parameters for the following micrographs: " << std::endl;
 		if (continue_old)
 			std::cout << " (skipping all micrographs for which a logfile with Final values already exists " << std::endl;
@@ -131,7 +131,7 @@ void CtffindRunner::run()
 		int barstep;
 		if (verb > 0)
 		{
-			std::cout << " Estimating CTF parameters using Niko Grigorieff's CTFFIND3 ..." << std::endl;
+			std::cout << " Estimating CTF parameters using Niko Grigorieff's CTFFIND ..." << std::endl;
 			init_progress_bar(fn_micrographs.size());
 			barstep = XMIPP_MAX(1, fn_micrographs.size() / 60);
 		}
@@ -141,7 +141,7 @@ void CtffindRunner::run()
 			if (verb > 0 && imic % barstep == 0)
 				progress_bar(imic);
 
-			executeCtffind3(fn_micrographs[imic]);
+			executeCtffind(fn_micrographs[imic]);
 		}
 
 		if (verb > 0)
@@ -158,12 +158,12 @@ void CtffindRunner::joinCtffindResults()
 	for (long int imic = 0; imic < fn_micrographs.size(); imic++)
     {
 		FileName fn_microot = fn_micrographs[imic].without(".mrc");
-		double defU, defV, defAng, CC, HT, CS, AmpCnst, XMAG, DStep;
-		bool has_this_ctf = getCtffind3Results(fn_microot, defU, defV, defAng, CC,
+		DOUBLE defU, defV, defAng, CC, HT, CS, AmpCnst, XMAG, DStep;
+		bool has_this_ctf = getCtffindResults(fn_microot, defU, defV, defAng, CC,
 				HT, CS, AmpCnst, XMAG, DStep);
 
 		if (!has_this_ctf)
-			REPORT_ERROR("CtffindRunner::joinCtffind3Results ERROR; cannot get CTF values for");
+			REPORT_ERROR("CtffindRunner::joinCtffindResults ERROR; cannot get CTF values for");
 
 		FileName fn_ctf = fn_microot + ".ctf:mrc";
 		MDctf.addObject();
@@ -182,7 +182,7 @@ void CtffindRunner::joinCtffindResults()
 	MDctf.write(fn_out);
 }
 
-void CtffindRunner::executeCtffind3(FileName fn_mic)
+void CtffindRunner::executeCtffind(FileName fn_mic)
 {
 
 	FileName fn_root = fn_mic.withoutExtension();
@@ -194,7 +194,7 @@ void CtffindRunner::executeCtffind3(FileName fn_mic)
 	std::ofstream  fh;
 	fh.open((fn_script).c_str(), std::ios::out);
 	if (!fh)
-	 REPORT_ERROR( (std::string)"CtffindRunner::execute_ctffind3 cannot create file: " + fn_script);
+	 REPORT_ERROR( (std::string)"CtffindRunner::execute_ctffind cannot create file: " + fn_script);
 
         // If given, then put a square window of ctf_win on the micrograph for CTF estimation
         if (ctf_win > 0)
@@ -202,12 +202,12 @@ void CtffindRunner::executeCtffind3(FileName fn_mic)
             // Window micrograph to a smaller, squared sub-micrograph to estimate CTF on
             fn_mic_win = fn_root + "_win.mrc";
             // Read in micrograph, window and write out again
-            Image<double> I;
+            Image<DOUBLE> I;
             I.read(fn_mic);
             I().setXmippOrigin();
             I().window(FIRST_XMIPP_INDEX(ctf_win), FIRST_XMIPP_INDEX(ctf_win), LAST_XMIPP_INDEX(ctf_win), LAST_XMIPP_INDEX(ctf_win));
             // Calculate mean, stddev, min and max
-            double avg, stddev, minval, maxval;
+            DOUBLE avg, stddev, minval, maxval;
             I().computeStats(avg, stddev, minval, maxval);
             I.MDMainHeader.setValue(EMDL_IMAGE_STATS_MIN, minval);
             I.MDMainHeader.setValue(EMDL_IMAGE_STATS_MAX, maxval);
@@ -218,9 +218,9 @@ void CtffindRunner::executeCtffind3(FileName fn_mic)
         else
             fn_mic_win = fn_mic;
 
-	// Write script to run x3dna
+	// Write script to run ctffind
 	fh << "#!/usr/bin/env csh"<<std::endl;
-	fh << fn_ctffind3_exe << " > " << fn_log << " << EOF"<<std::endl;
+	fh << fn_ctffind_exe << " > " << fn_log << " << EOF"<<std::endl;
 	fh << fn_mic_win << std::endl;
 	fh << fn_ctf << std::endl;
 	// CS[mm], HT[kV], AmpCnst, XMAG, DStep[um]
@@ -230,9 +230,9 @@ void CtffindRunner::executeCtffind3(FileName fn_mic)
 	fh <<"EOF"<<std::endl;
 	fh.close();
 
-	// Execute ctffind3
+	// Execute ctffind
 	if (!system(NULL))
-	 REPORT_ERROR("There is a problem with the system call to run ctffind3");
+	 REPORT_ERROR("There is a problem with the system call to run ctffind");
 	FileName fn_cmnd = "csh "+ fn_script;
 	system ( fn_cmnd.c_str() );
 
@@ -245,8 +245,8 @@ void CtffindRunner::executeCtffind3(FileName fn_mic)
 
 }
 
-bool getCtffind3Results(FileName fn_microot, double &defU, double &defV, double &defAng, double &CC,
-		double &HT, double &CS, double &AmpCnst, double &XMAG, double &DStep, bool die_if_not_found)
+bool getCtffindResults(FileName fn_microot, DOUBLE &defU, DOUBLE &defV, DOUBLE &defAng, DOUBLE &CC,
+		DOUBLE &HT, DOUBLE &CS, DOUBLE &AmpCnst, DOUBLE &XMAG, DOUBLE &DStep, bool die_if_not_found)
 {
 
 	FileName fn_log = fn_microot + "_ctffind3.log";
diff --git a/src/ctffind_runner.h b/src/ctffind_runner.h
index eb55415..c759019 100644
--- a/src/ctffind_runner.h
+++ b/src/ctffind_runner.h
@@ -49,39 +49,39 @@ public:
 	// Dimension of squared area of the micrograph to use for CTF estimation
 	int ctf_win;
 
-	// CTFFIND3 executable
-	FileName fn_ctffind3_exe;
+	// CTFFIND executable
+	FileName fn_ctffind_exe;
 
 	// Continue an old run: only estimate CTF if logfile WITH Final Values line does not yet exist, otherwise skip the micrograph
 	bool continue_old;
 
-	////// CTFFIND3 parameters
+	////// CTFFIND parameters
 	// Size of the box to calculate FFTw
-	double box_size;
+	DOUBLE box_size;
 
 	// Minimum and maximum resolution (in A) to be taken into account
-	double resol_min, resol_max;
+	DOUBLE resol_min, resol_max;
 
 	// Defocus search parameters (in A, positive is underfocus)
-	double min_defocus, max_defocus, step_defocus;
+	DOUBLE min_defocus, max_defocus, step_defocus;
 
 	// Amount of astigmatism (in A)
-	double amount_astigmatism;
+	DOUBLE amount_astigmatism;
 
 	// Voltage (kV)
-	double Voltage;
+	DOUBLE Voltage;
 
 	// Spherical aberration
-	double Cs;
+	DOUBLE Cs;
 
 	// Amplitude contrast (e.g. 0.07)
-	double AmplitudeConstrast;
+	DOUBLE AmplitudeConstrast;
 
 	// Magnification
-	double Magnification;
+	DOUBLE Magnification;
 
 	// Detector pixel size (um)
-	double PixelSize;
+	DOUBLE PixelSize;
 
 	// Flag to only join results into a star file
 	bool do_only_join_results;
@@ -96,20 +96,20 @@ public:
 	// Initialise some stuff after reading
 	void initialise();
 
-	// Execute all CTFFIND3 jobs to get CTF parameters
+	// Execute all CTFFIND jobs to get CTF parameters
 	void run();
 
-	// Harvest all CTFFIND3 results into a single STAR file
+	// Harvest all CTFFIND results into a single STAR file
 	void joinCtffindResults();
 
-	// Execute CTFFIND3 for a single micrograph
-	void executeCtffind3(FileName fn_mic);
+	// Execute CTFFIND for a single micrograph
+	void executeCtffind(FileName fn_mic);
 
 };
 
 // Get micrograph metadata
-bool getCtffind3Results(FileName fn_mic, double &defU, double &defV, double &defAng, double &CC,
-		double &HT, double &CS, double &AmpCnst, double &XMAG, double &DStep, bool die_if_not_found = true);
+bool getCtffindResults(FileName fn_mic, DOUBLE &defU, DOUBLE &defV, DOUBLE &defAng, DOUBLE &CC,
+		DOUBLE &HT, DOUBLE &CS, DOUBLE &AmpCnst, DOUBLE &XMAG, DOUBLE &DStep, bool die_if_not_found = true);
 
 
 #endif /* CTFFIND_RUNNER_H_ */
diff --git a/src/ctffind_runner_mpi.cpp b/src/ctffind_runner_mpi.cpp
index 4913e42..1b5df0b 100644
--- a/src/ctffind_runner_mpi.cpp
+++ b/src/ctffind_runner_mpi.cpp
@@ -50,7 +50,7 @@ void CtffindRunnerMpi::run()
 		int barstep;
 		if (verb > 0)
 		{
-			std::cout << " Estimating CTF parameters using Niko Grigorieff's CTFFIND3 ..." << std::endl;
+			std::cout << " Estimating CTF parameters using Niko Grigorieff's CTFFIND ..." << std::endl;
 			init_progress_bar(my_nr_micrographs);
 			barstep = XMIPP_MAX(1, my_nr_micrographs / 60);
 		}
@@ -60,7 +60,7 @@ void CtffindRunnerMpi::run()
 			if (verb > 0 && imic % barstep == 0)
 				progress_bar(imic);
 
-			executeCtffind3(fn_micrographs[imic]);
+			executeCtffind(fn_micrographs[imic]);
 		}
 		if (verb > 0)
 			progress_bar(my_nr_micrographs);
diff --git a/src/displayer.cpp b/src/displayer.cpp
index b155974..d7a698a 100644
--- a/src/displayer.cpp
+++ b/src/displayer.cpp
@@ -53,8 +53,8 @@ void DisplayBox::draw()
     //fl_pop_clip();
 }
 
-void DisplayBox::setData(MultidimArray<double> &img, MetaDataContainer *MDCin, int _ipos,
-		double _minval, double _maxval, double _scale, bool do_relion_scale)
+void DisplayBox::setData(MultidimArray<DOUBLE> &img, MetaDataContainer *MDCin, int _ipos,
+		DOUBLE _minval, DOUBLE _maxval, DOUBLE _scale, bool do_relion_scale)
 {
 
 	scale = _scale;
@@ -70,7 +70,7 @@ void DisplayBox::setData(MultidimArray<double> &img, MetaDataContainer *MDCin, i
 	// For volumes only show the central slice
 	if (ZSIZE(img) > 1)
 	{
-		MultidimArray<double> slice;
+		MultidimArray<DOUBLE> slice;
 		img.getSlice(ZSIZE(img)/2, slice);
 		img=slice;
 	}
@@ -81,9 +81,9 @@ void DisplayBox::setData(MultidimArray<double> &img, MetaDataContainer *MDCin, i
 	xoff = (xsize_data < w() ) ? (w() - xsize_data) / 2 : 0;
 	yoff = (ysize_data < h() ) ? (h() - ysize_data) / 2 : 0;
 	img_data = new char [xsize_data * ysize_data];
-	double range = maxval - minval;
-	double step = range / 255; // 8-bit scaling range from 0 to 255
-	double* old_ptr=NULL;
+	DOUBLE range = maxval - minval;
+	DOUBLE step = range / 255; // 8-bit scaling range from 0 to 255
+	DOUBLE* old_ptr=NULL;
     long int n;
 
     // For micrographs use relion-scaling to avoid bias in down-sampled positions
@@ -163,7 +163,7 @@ bool DisplayBox::unSelect()
 }
 
 int basisViewerWindow::fillCanvas(int viewer_type, MetaDataTable &MDin, EMDLabel display_label, bool _do_read_whole_stacks, bool _do_apply_orient,
-		double _minval, double _maxval, double _sigma_contrast, double _scale, double _ori_scale, int _ncol, bool _do_class,
+		DOUBLE _minval, DOUBLE _maxval, DOUBLE _sigma_contrast, DOUBLE _scale, DOUBLE _ori_scale, int _ncol, bool _do_class,
 		MetaDataTable *_MDdata, int _nr_regroup, bool _is_data, MetaDataTable *_MDgroups)
 {
     // Scroll bars
@@ -171,14 +171,14 @@ int basisViewerWindow::fillCanvas(int viewer_type, MetaDataTable &MDin, EMDLabel
 
     // Pre-set the canvas to the correct size
     FileName fn_img;
-    Image<double> img;
+    Image<DOUBLE> img;
     MDin.getValue(display_label, fn_img);
 	img.read(fn_img, false);
 	int nimgs = MDin.numberOfObjects();
 	if (viewer_type == MULTIVIEWER)
 	{
 		int xsize_canvas = _ncol * (CEIL(XSIZE(img())*_scale) + BOX_OFFSET);
-		int nrow = CEIL((double)nimgs/_ncol);
+		int nrow = CEIL((DOUBLE)nimgs/_ncol);
 		int ysize_canvas = nrow * (CEIL(YSIZE(img())*_scale) + BOX_OFFSET);
 		multiViewerCanvas canvas(0, 0, xsize_canvas, ysize_canvas);
 		canvas.SetScroll(&scroll);
@@ -220,9 +220,9 @@ int basisViewerWindow::fillCanvas(int viewer_type, MetaDataTable &MDin, EMDLabel
 	}
 }
 
-int basisViewerWindow::fillPickerViewerCanvas(MultidimArray<double> image, double _minval, double _maxval, double _sigma_contrast,
-		double _scale, int _particle_radius, FileName _fn_coords,
-		FileName _fn_color, FileName _fn_mic, FileName _color_label, double _color_blue_value, double _color_red_value)
+int basisViewerWindow::fillPickerViewerCanvas(MultidimArray<DOUBLE> image, DOUBLE _minval, DOUBLE _maxval, DOUBLE _sigma_contrast,
+		DOUBLE _scale, int _particle_radius, FileName _fn_coords,
+		FileName _fn_color, FileName _fn_mic, FileName _color_label, DOUBLE _color_blue_value, DOUBLE _color_red_value)
 {
     // Scroll bars
     Fl_Scroll scroll(0, 0, w(), h());
@@ -251,7 +251,7 @@ int basisViewerWindow::fillPickerViewerCanvas(MultidimArray<double> image, doubl
 
 }
 
-int basisViewerWindow::fillSingleViewerCanvas(MultidimArray<double> image, double _minval, double _maxval, double _sigma_contrast, double _scale)
+int basisViewerWindow::fillSingleViewerCanvas(MultidimArray<DOUBLE> image, DOUBLE _minval, DOUBLE _maxval, DOUBLE _sigma_contrast, DOUBLE _scale)
 {
     // Scroll bars
     Fl_Scroll scroll(0, 0, w(), h());
@@ -268,8 +268,8 @@ int basisViewerWindow::fillSingleViewerCanvas(MultidimArray<double> image, doubl
 	return Fl::run();
 
 }
-int basisViewerCanvas::fill(MetaDataTable &MDin, EMDLabel display_label, bool _do_apply_orient, double _minval, double _maxval,
-		double _sigma_contrast, double _scale, int _ncol)
+int basisViewerCanvas::fill(MetaDataTable &MDin, EMDLabel display_label, bool _do_apply_orient, DOUBLE _minval, DOUBLE _maxval,
+		DOUBLE _sigma_contrast, DOUBLE _scale, int _ncol)
 {
 
 	ncol = _ncol;
@@ -325,7 +325,7 @@ int basisViewerCanvas::fill(MetaDataTable &MDin, EMDLabel display_label, bool _d
 		if (fn_next_stack != fn_my_stack)
 		{
 
-			Image<double> stack, img;
+			Image<DOUBLE> stack, img;
 			fImageHandler* hFile;
 			if (do_read_whole_stacks)
 				// Read the entire stack into memory
@@ -346,22 +346,22 @@ int basisViewerCanvas::fill(MetaDataTable &MDin, EMDLabel display_label, bool _d
 
 				if (_do_apply_orient)
 				{
-					double psi;
-					Matrix1D<double> offset(2);
+					DOUBLE psi;
+					Matrix1D<DOUBLE> offset(2);
 					MDin.getValue(EMDL_ORIENT_PSI, psi, my_ipos);
 					MDin.getValue(EMDL_ORIENT_ORIGIN_X, XX(offset), my_ipos);
 					MDin.getValue(EMDL_ORIENT_ORIGIN_Y, YY(offset), my_ipos);
 
-					Matrix2D<double> A;
+					Matrix2D<DOUBLE> A;
 					rotation2DMatrix(psi, A);
-				    MAT_ELEM(A,0, 2) = XX(offset);
-				    MAT_ELEM(A,1, 2) = YY(offset);
-				    selfApplyGeometry(img(), A, IS_NOT_INV, DONT_WRAP);
+                                        MAT_ELEM(A, 0, 2) = COSD(psi) * XX(offset) - SIND(psi) * YY(offset);
+                                        MAT_ELEM(A, 1, 2) = COSD(psi) * YY(offset) + SIND(psi) * XX(offset);
+                                        selfApplyGeometry(img(), A, IS_NOT_INV, DONT_WRAP);
 				}
 
 				// Dont change the user-provided _minval and _maxval in the getImageContrast routine!
-				double myminval = _minval;
-				double mymaxval = _maxval;
+				DOUBLE myminval = _minval;
+				DOUBLE mymaxval = _maxval;
 				getImageContrast(img(), myminval, mymaxval, _sigma_contrast);
 
 				long int my_sorted_ipos = my_ipos;
@@ -410,7 +410,7 @@ int basisViewerCanvas::fill(MetaDataTable &MDin, EMDLabel display_label, bool _d
 
 
 }
-int basisViewerCanvas::fill(MultidimArray<double> &image, double _minval, double _maxval, double _sigma_contrast, double _scale)
+int basisViewerCanvas::fill(MultidimArray<DOUBLE> &image, DOUBLE _minval, DOUBLE _maxval, DOUBLE _sigma_contrast, DOUBLE _scale)
 {
 	xoff = yoff = 0;
 	nrow = ncol = 1;
@@ -428,14 +428,14 @@ int basisViewerCanvas::fill(MultidimArray<double> &image, double _minval, double
 
 }
 
-void basisViewerCanvas::getImageContrast(MultidimArray<double> &image, double &minval, double &maxval, double &sigma_contrast)
+void basisViewerCanvas::getImageContrast(MultidimArray<DOUBLE> &image, DOUBLE &minval, DOUBLE &maxval, DOUBLE &sigma_contrast)
 {
 	// First check whether to apply sigma-contrast, i.e. set minval and maxval to the mean +/- sigma_contrast times the stddev
 	bool redo_minmax = (sigma_contrast > 0. || minval != maxval);
 
 	if (sigma_contrast > 0. || minval == maxval)
 	{
-		double avg, stddev;
+		DOUBLE avg, stddev;
 		image.computeStats(avg, stddev, minval, maxval);
 		if (sigma_contrast > 0.)
 		{
@@ -449,7 +449,7 @@ void basisViewerCanvas::getImageContrast(MultidimArray<double> &image, double &m
 	{
 		FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(image)
 		{
-			double val = DIRECT_MULTIDIM_ELEM(image, n);
+			DOUBLE val = DIRECT_MULTIDIM_ELEM(image, n);
 			if (val > maxval)
 				DIRECT_MULTIDIM_ELEM(image, n) = maxval;
 			else if (val < minval)
@@ -686,8 +686,8 @@ void multiViewerCanvas::showAverage(bool selected, bool show_stddev)
 {
 	int xsize = boxes[0]->xsize_data;
 	int ysize = boxes[0]->ysize_data;
-	MultidimArray<double> sum(ysize, xsize);
-	MultidimArray<double> sum2(ysize, xsize);
+	MultidimArray<DOUBLE> sum(ysize, xsize);
+	MultidimArray<DOUBLE> sum2(ysize, xsize);
 
 	int nn = 0;
 	for (long int ipos = 0; ipos < boxes.size(); ipos++)
@@ -745,7 +745,7 @@ void multiViewerCanvas::showOriginalImage(int ipos)
 	/*
 	FileName fn_img;
 	boxes[ipos]->MDimg.getValue(display_label, fn_img);
-	Image<double> img;
+	Image<DOUBLE> img;
 	img.read(fn_img);
     basisViewerWindow win(CEIL(ori_scale*XSIZE(img())), CEIL(ori_scale*YSIZE(img())), fn_img.c_str());
     if (sigma_contrast > 0.)
@@ -811,6 +811,9 @@ void regroupSelectedParticles(MetaDataTable &MDdata, MetaDataTable &MDgroups, in
 	if (nr_regroups <= 0)
 		return;
 
+	if (nr_regroups > 999)
+		REPORT_ERROR("regroupSelectedParticles: cannot regroup in more than 999 groups. Usually around 20-50 groups are useful.");
+
 	// First sort the MDgroups based on refined intensity scale factor
 	MDgroups.sort(EMDL_MLMODEL_GROUP_SCALE_CORRECTION);
 
@@ -931,8 +934,8 @@ int singleViewerCanvas::handle(int ev)
 		{
 			int ival = boxes[0]->img_data[ry*boxes[0]->xsize_data + rx];
 			if (ival < 0) ival += 256;
-			double step = (boxes[0]->maxval - boxes[0]->minval) / 255.;
-			double dval = ival * step + boxes[0]->minval;
+			DOUBLE step = (boxes[0]->maxval - boxes[0]->minval) / 255.;
+			DOUBLE dval = ival * step + boxes[0]->minval;
 			int ysc = ROUND(ry/boxes[0]->scale);
 			int xsc = ROUND(rx/boxes[0]->scale);
 			int yscp = ysc - ROUND((boxes[0]->ysize_data/(2.* boxes[0]->scale)));
@@ -991,7 +994,7 @@ int singleViewerCanvas::handle(int ev)
 			fl_circle(postdrag_xc, postdrag_yc, 3);
 			int dx = postdrag_xc - predrag_xc;
 			int dy = postdrag_yc - predrag_yc;
-			double dist = sqrt((double)(dx*dx + dy*dy));
+			DOUBLE dist = sqrt((DOUBLE)(dx*dx + dy*dy));
 			std::string text =  floatToString(dist/boxes[0]->scale) + " pixels";
 			fl_draw(text.c_str(), (postdrag_xc + predrag_xc)/2, (postdrag_yc + predrag_yc)/2);
 			// Also write to the screen, in case the text falls outside the screen
@@ -1013,22 +1016,22 @@ void singleViewerCanvas::printHelp()
 
 void pickerViewerCanvas::draw()
 {
-	double scale = boxes[0]->scale;
+	DOUBLE scale = boxes[0]->scale;
 
 	FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDcoords)
 	{
-		double xcoor, ycoor;
+		DOUBLE xcoor, ycoor;
 		MDcoords.getValue(EMDL_IMAGE_COORD_X, xcoor);
 		MDcoords.getValue(EMDL_IMAGE_COORD_Y, ycoor);
 
 		if (color_label != EMDL_UNDEFINED)
 		{
-			double colval;
+			DOUBLE colval;
 			if (EMDL::isInt(color_label))
 			{
 				int ival;
 				MDcoords.getValue(color_label, ival);
-				colval = (double)ival;
+				colval = (DOUBLE)ival;
 			}
 			else
 			{
@@ -1073,17 +1076,17 @@ int pickerViewerCanvas::handle(int ev)
 
 	if (ev==FL_PUSH || (ev==FL_DRAG && Fl::event_button() == FL_MIDDLE_MOUSE) )
 	{
-		double scale = boxes[0]->scale;
+		DOUBLE scale = boxes[0]->scale;
 		int xc = (int)Fl::event_x() - scroll->x() + scroll->hscrollbar.value();
 		int yc = (int)Fl::event_y() - scroll->y() + scroll->scrollbar.value();
-		double xcoor = (double)ROUND(xc/scale);
-		double ycoor = (double)ROUND(yc/scale);
-		double rad2 = particle_radius*particle_radius/(scale*scale);
+		DOUBLE xcoor = (DOUBLE)ROUND(xc/scale);
+		DOUBLE ycoor = (DOUBLE)ROUND(yc/scale);
+		DOUBLE rad2 = particle_radius*particle_radius/(scale*scale);
 		if (Fl::event_button() == FL_LEFT_MOUSE)
 		{
 			// Left mouse for picking
 			// Check the pick is not inside an existing circle
-			double xcoor_p, ycoor_p;
+			DOUBLE xcoor_p, ycoor_p;
 			FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDcoords)
 			{
 				MDcoords.getValue(EMDL_IMAGE_COORD_X, xcoor_p);
@@ -1107,17 +1110,17 @@ int pickerViewerCanvas::handle(int ev)
 			}
 			if (MDcoords.containsLabel(EMDL_ORIENT_PSI))
 			{
-				double psi = -999.;
+				DOUBLE psi = -999.;
 				MDcoords.setValue(EMDL_ORIENT_PSI, psi);
 			}
 			if (MDcoords.containsLabel(EMDL_PARTICLE_AUTOPICK_FOM))
 			{
-				double fom = -999.;
+				DOUBLE fom = -999.;
 				MDcoords.setValue(EMDL_PARTICLE_AUTOPICK_FOM, fom);
 			}
 			if (MDcoords.containsLabel(color_label))
 			{
-				double z = -999.;
+				DOUBLE z = -999.;
 				MDcoords.setValue(color_label, z);
 			}
 
@@ -1128,7 +1131,7 @@ int pickerViewerCanvas::handle(int ev)
 		{
 			boxes[0]->redraw();
 			// Middle mouse for deleting
-			double xcoor_p, ycoor_p;
+			DOUBLE xcoor_p, ycoor_p;
 			FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDcoords)
 			{
 				MDcoords.getValue(EMDL_IMAGE_COORD_X, xcoor_p);
@@ -1275,7 +1278,7 @@ void pickerViewerCanvas::findColorColumnForCoordinates()
 	}
 	else
 	{
-		double val = -999.;
+		DOUBLE val = -999.;
 		FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDcoords)
 		{
 			MDcoords.setValue(color_label, val);
@@ -1296,11 +1299,11 @@ void pickerViewerCanvas::findColorColumnForCoordinates()
 			iimg--; // counting starts at 1 in STAR file!
 
 			// Check that this entry in the coord file has the same xpos and ypos
-			double my_xpos, my_ypos;
+			DOUBLE my_xpos, my_ypos;
 			MDcoords.getValue(EMDL_IMAGE_COORD_X, my_xpos, iimg);
 			MDcoords.getValue(EMDL_IMAGE_COORD_Y, my_ypos, iimg);
 
-			double x, y;
+			DOUBLE x, y;
 			MDcolor.getValue(EMDL_IMAGE_COORD_X, x);
 			MDcolor.getValue(EMDL_IMAGE_COORD_Y, y);
 
@@ -1321,7 +1324,7 @@ void pickerViewerCanvas::findColorColumnForCoordinates()
 				}
 				else
 				{
-					double val;
+					DOUBLE val;
 					MDcolor.getValue(color_label, val);
 					MDcoords.setValue(color_label, val, iimg);
 				}
@@ -1839,7 +1842,7 @@ int Displayer::runGui()
 	else
 	{
 		// Try reading as an image/stack header
-		Image<double> img;
+		Image<DOUBLE> img;
 		img.read(_fn_in, false);
 		win.is_multi = (ZSIZE(img()) * NSIZE(img()) > 1);
 	}
@@ -1856,7 +1859,7 @@ int Displayer::run()
     else if (do_pick)
     {
 
-        Image<double> img;
+        Image<DOUBLE> img;
         img.read(fn_in); // dont read data yet: only header to get size
 
         if (lowpass > 0.)
@@ -1901,7 +1904,7 @@ int Displayer::run()
     else
     {
         // Attempt to read a single-file image
-        Image<double> img;
+        Image<DOUBLE> img;
         img.read(fn_in, false); // dont read data yet: only header to get size
 
         MDin.clear();
@@ -1929,7 +1932,7 @@ int Displayer::run()
         	// Use a single minval and maxval for all slice
         	if (minval == maxval)
         	{
-        		Image<double> It;
+        		Image<DOUBLE> It;
         		It.read(fn_in);
         		It().computeDoubleMinMax(minval, maxval);
         	}
diff --git a/src/displayer.h b/src/displayer.h
index bdaa4ff..f7adeb9 100644
--- a/src/displayer.h
+++ b/src/displayer.h
@@ -44,7 +44,7 @@
 
 #define GUI_BACKGROUND_COLOR (fl_rgb_color(240,240,240))
 #define GUI_INPUT_COLOR (fl_rgb_color(255,255,230))
-#define GUI_RUNBUTTON_COLOR (fl_rgb_color(255,80,80))
+#define GUI_RUNBUTTON_COLOR (fl_rgb_color(205,40,150))
 
 #define SELECTED true
 #define NOTSELECTED false
@@ -91,15 +91,15 @@ public:
 	char * img_data;
 
 	// For getting back close the original image values from the uchar ones...
-	double minval;
-	double maxval;
-	double scale;
+	DOUBLE minval;
+	DOUBLE maxval;
+	DOUBLE scale;
 
 	// Constructor with an image and its metadata
 	DisplayBox(int X, int Y, int W, int H, const char *L=0) : Fl_Box(X,Y,W,H,L) { img_data = NULL; MDimg.clear(); }
 
-	void setData(MultidimArray<double> &img, MetaDataContainer *MDCin, int ipos, double minval, double maxval,
-			double _scale, bool do_relion_scale = false);
+	void setData(MultidimArray<DOUBLE> &img, MetaDataContainer *MDCin, int ipos, DOUBLE minval, DOUBLE maxval,
+			DOUBLE _scale, bool do_relion_scale = false);
 
 	// Destructor
 	~DisplayBox()
@@ -129,13 +129,13 @@ public:
 	basisViewerWindow(int W, int H, const char* title=0): Fl_Window(W, H, title){}
 
 	int fillCanvas(int viewer_type, MetaDataTable &MDin, EMDLabel display_label, bool _do_read_whole_stacks, bool _do_apply_orient,
-			double _minval, double _maxval, double _sigma_contrast,
-			double _scale, double _ori_scale, int _ncol, bool do_class = false, MetaDataTable *MDdata = NULL,
+			DOUBLE _minval, DOUBLE _maxval, DOUBLE _sigma_contrast,
+			DOUBLE _scale, DOUBLE _ori_scale, int _ncol, bool do_class = false, MetaDataTable *MDdata = NULL,
 			int _nr_regroup = -1, bool _is_data = false, MetaDataTable *MDgroups = NULL);
-	int fillSingleViewerCanvas(MultidimArray<double> image, double _minval, double _maxval, double _sigma_contrast, double _scale);
-	int fillPickerViewerCanvas(MultidimArray<double> image, double _minval, double _maxval, double _sigma_contrast, double _scale,
+	int fillSingleViewerCanvas(MultidimArray<DOUBLE> image, DOUBLE _minval, DOUBLE _maxval, DOUBLE _sigma_contrast, DOUBLE _scale);
+	int fillPickerViewerCanvas(MultidimArray<DOUBLE> image, DOUBLE _minval, DOUBLE _maxval, DOUBLE _sigma_contrast, DOUBLE _scale,
 			int _particle_radius, FileName _fn_coords = "",
-			FileName _fn_color = "", FileName _fn_mic= "", FileName _color_label = "", double _color_blue_value = 0., double _color_red_value = 1.);
+			FileName _fn_color = "", FileName _fn_mic= "", FileName _color_label = "", DOUBLE _color_blue_value = 0., DOUBLE _color_red_value = 1.);
 
 
 };
@@ -169,12 +169,12 @@ public:
 
 	void SetScroll(Fl_Scroll *val) { scroll = val; }
 
-	int fill(MetaDataTable &MDin, EMDLabel display_label, bool _do_apply_orient, double _minval, double _maxval,
-			double _sigma_contrast, double _scale, int _ncol);
-	int fill(MultidimArray<double> &image, double _minval, double _maxval, double _sigma_contrast, double _scale = 1.);
+	int fill(MetaDataTable &MDin, EMDLabel display_label, bool _do_apply_orient, DOUBLE _minval, DOUBLE _maxval,
+			DOUBLE _sigma_contrast, DOUBLE _scale, int _ncol);
+	int fill(MultidimArray<DOUBLE> &image, DOUBLE _minval, DOUBLE _maxval, DOUBLE _sigma_contrast, DOUBLE _scale = 1.);
 
 private:
-	void getImageContrast(MultidimArray<double> &image, double &minval, double &maxval, double &sigma_contrast);
+	void getImageContrast(MultidimArray<DOUBLE> &image, DOUBLE &minval, DOUBLE &maxval, DOUBLE &sigma_contrast);
 
 };
 
@@ -202,13 +202,13 @@ public:
 	MetaDataTable *MDgroups;
 
 	// Scale for showing the original image
-	double ori_scale;
+	DOUBLE ori_scale;
 
 	// To know which original image to display
 	EMDLabel display_label;
 
 	// To know which contrast to apply to original image display
-	double sigma_contrast;
+	DOUBLE sigma_contrast;
 
 	// Constructor with w x h size of the window and a title
 	multiViewerCanvas(int X,int Y, int W, int H, const char* title=0): basisViewerCanvas(X,Y,W, H, title) { }
@@ -279,10 +279,10 @@ public:
 	EMDLabel color_label;
 
 	// Blue value for coloring
-	double smallest_color_value;
+	DOUBLE smallest_color_value;
 
 	// Red value for coloring
-	double biggest_color_value;
+	DOUBLE biggest_color_value;
 
 	// Red->Blue is true; blue->red is false
 	bool do_blue_to_red;
@@ -310,13 +310,13 @@ private:
 class popupInputWindow : Fl_Window
 {
 	Fl_Input * input;
-	double result;
+	DOUBLE result;
 public:
 
 	// Constructor with w x h size of the window and a title
 	popupInputWindow(int W, int H, const char* title=0): Fl_Window(W, H, title){}
 
-	int fill(std::string label, double &_result)
+	int fill(std::string label, DOUBLE &_result)
 	{
 		input = new Fl_Input(x() + 200, 0, 100, 30, "black value: ") ;
 		Fl_Button * done = new Fl_Button(x()+350, 0, 30, 30, "go!");
@@ -400,7 +400,7 @@ public:
 	bool reverse_sort;
 
 	// Scale factor for displaying
-	double scale;
+	DOUBLE scale;
 
 	// Number of rows for tiled view
 	int nrow, ncol;
@@ -409,13 +409,13 @@ public:
 	bool do_apply_orient;
 
 	// Scale for showing the original image
-	double ori_scale;
+	DOUBLE ori_scale;
 
 	// Black and white values
-	double minval, maxval;
+	DOUBLE minval, maxval;
 
 	// For setting black and white contrast to a specified times the image standard deviation from the mean
-	double sigma_contrast;
+	DOUBLE sigma_contrast;
 
 	// Particle diameter
 	int particle_radius;
@@ -433,7 +433,7 @@ public:
 	FileName color_label;
 
 	// Values for blue and red coloring
-	double color_blue_value, color_red_value;
+	DOUBLE color_blue_value, color_red_value;
 
 	// Tablename to read from in the input STAR file
 	FileName table_name;
@@ -463,10 +463,10 @@ public:
 	std::vector<DisplayBox*> boxes;
 
 	// Lowpass filter for picker images
-	double lowpass;
+	DOUBLE lowpass;
 
 	// Pixel size to calculate lowpass filter in Angstroms
-	double angpix;
+	DOUBLE angpix;
 
 public:
 	// Read command line arguments
diff --git a/src/euler.cpp b/src/euler.cpp
index a39e738..ab28801 100644
--- a/src/euler.cpp
+++ b/src/euler.cpp
@@ -49,11 +49,11 @@
 #include "src/funcs.h"
 
 /* Euler angles --> matrix ------------------------------------------------- */
-void Euler_angles2matrix(double alpha, double beta, double gamma,
-                         Matrix2D<double> &A, bool homogeneous)
+void Euler_angles2matrix(DOUBLE alpha, DOUBLE beta, DOUBLE gamma,
+                         Matrix2D<DOUBLE> &A, bool homogeneous)
 {
-    double ca, sa, cb, sb, cg, sg;
-    double cc, cs, sc, ss;
+    DOUBLE ca, sa, cb, sb, cg, sg;
+    DOUBLE cc, cs, sc, ss;
 
     if (homogeneous)
     {
@@ -91,11 +91,11 @@ void Euler_angles2matrix(double alpha, double beta, double gamma,
 }
 
 /* Euler direction --------------------------------------------------------- */
-void Euler_angles2direction(double alpha, double beta,
-						    Matrix1D<double> &v)
+void Euler_angles2direction(DOUBLE alpha, DOUBLE beta,
+						    Matrix1D<DOUBLE> &v)
 {
-    double ca, sa, cb, sb;
-    double sc, ss;
+    DOUBLE ca, sa, cb, sb;
+    DOUBLE sc, ss;
 
     v.resize(3);
     alpha = DEG2RAD(alpha);
@@ -116,15 +116,15 @@ void Euler_angles2direction(double alpha, double beta,
 /* Euler direction2angles ------------------------------- */
 //gamma is useless but I keep it for simmetry
 //with Euler_direction
-void Euler_direction2angles(Matrix1D<double> &v0,
-                            double &alpha, double &beta)
+void Euler_direction2angles(Matrix1D<DOUBLE> &v0,
+                            DOUBLE &alpha, DOUBLE &beta)
 {
-    double abs_ca, sb, cb;
-    double aux_alpha;
-    double aux_beta;
-    double error, newerror;
-    Matrix1D<double> v_aux;
-    Matrix1D<double> v;
+    DOUBLE abs_ca, sb, cb;
+    DOUBLE aux_alpha;
+    DOUBLE aux_beta;
+    DOUBLE error, newerror;
+    Matrix1D<DOUBLE> v_aux;
+    Matrix1D<DOUBLE> v;
 
     //if not normalized do it so
     v.resize(3);
@@ -209,10 +209,10 @@ void Euler_direction2angles(Matrix1D<double> &v0,
 /* Matrix --> Euler angles ------------------------------------------------- */
 #define CHECK
 //#define DEBUG_EULER
-void Euler_matrix2angles(const Matrix2D<double> &A, double &alpha,
-                         double &beta, double &gamma)
+void Euler_matrix2angles(const Matrix2D<DOUBLE> &A, DOUBLE &alpha,
+                         DOUBLE &beta, DOUBLE &gamma)
 {
-    double abs_sb, sign_sb;
+    DOUBLE abs_sb, sign_sb;
 
     if (MAT_XSIZE(A) != 3 || MAT_YSIZE(A) != 3)
         REPORT_ERROR( "Euler_matrix2angles: The Euler matrix is not 3x3");
@@ -251,23 +251,6 @@ void Euler_matrix2angles(const Matrix2D<double> &A, double &alpha,
     beta  = RAD2DEG(beta);
     alpha = RAD2DEG(alpha);
 
-#ifdef double
-
-    Matrix2D<double> Ap;
-    Euler_angles2matrix(alpha, beta, gamma, Ap);
-    if (A != Ap)
-    {
-        std::cout << "---\n";
-        std::cout << "Euler_matrix2angles: I have computed angles "
-        " which doesn't match with the original matrix\n";
-        std::cout << "Original matrix\n" << A;
-        std::cout << "Computed angles alpha=" << alpha << " beta=" << beta
-        << " gamma=" << gamma << std::endl;
-        std::cout << "New matrix\n" << Ap;
-        std::cout << "---\n";
-    }
-#endif
-
 #ifdef DEBUG_EULER
     std::cout << "abs_sb " << abs_sb << std::endl;
     std::cout << "A(1,2) " << A(1, 2) << " A(0,2) " << A(0, 2) << " gamma "
@@ -283,10 +266,10 @@ void Euler_matrix2angles(const Matrix2D<double> &A, double &alpha,
 
 #ifdef NEVERDEFINED
 // Michael's method
-void Euler_matrix2angles(Matrix2D<double> A, double *alpha, double *beta,
-                         double *gamma)
+void Euler_matrix2angles(Matrix2D<DOUBLE> A, DOUBLE *alpha, DOUBLE *beta,
+                         DOUBLE *gamma)
 {
-    double abs_sb;
+    DOUBLE abs_sb;
 
     if (ABS(A(1, 1)) > FLT_EPSILON)
     {
@@ -322,8 +305,8 @@ void Euler_matrix2angles(Matrix2D<double> A, double *alpha, double *beta,
 }
 #endif
 /* Euler up-down correction ------------------------------------------------ */
-void Euler_up_down(double rot, double tilt, double psi,
-                   double &newrot, double &newtilt, double &newpsi)
+void Euler_up_down(DOUBLE rot, DOUBLE tilt, DOUBLE psi,
+                   DOUBLE &newrot, DOUBLE &newtilt, DOUBLE &newpsi)
 {
     newrot  = rot;
     newtilt = tilt + 180;
@@ -331,8 +314,8 @@ void Euler_up_down(double rot, double tilt, double psi,
 }
 
 /* Same view, differently expressed ---------------------------------------- */
-void Euler_another_set(double rot, double tilt, double psi,
-                       double &newrot, double &newtilt, double &newpsi)
+void Euler_another_set(DOUBLE rot, DOUBLE tilt, DOUBLE psi,
+                       DOUBLE &newrot, DOUBLE &newtilt, DOUBLE &newpsi)
 {
     newrot  = rot + 180;
     newtilt = -tilt;
@@ -340,8 +323,8 @@ void Euler_another_set(double rot, double tilt, double psi,
 }
 
 /* Euler mirror Y ---------------------------------------------------------- */
-void Euler_mirrorY(double rot, double tilt, double psi,
-                   double &newrot, double &newtilt, double &newpsi)
+void Euler_mirrorY(DOUBLE rot, DOUBLE tilt, DOUBLE psi,
+                   DOUBLE &newrot, DOUBLE &newtilt, DOUBLE &newpsi)
 {
     newrot  = rot;
     newtilt = tilt + 180;
@@ -349,8 +332,8 @@ void Euler_mirrorY(double rot, double tilt, double psi,
 }
 
 /* Euler mirror X ---------------------------------------------------------- */
-void Euler_mirrorX(double rot, double tilt, double psi,
-                   double &newrot, double &newtilt, double &newpsi)
+void Euler_mirrorX(DOUBLE rot, DOUBLE tilt, DOUBLE psi,
+                   DOUBLE &newrot, DOUBLE &newtilt, DOUBLE &newpsi)
 {
     newrot  = rot;
     newtilt = tilt + 180;
@@ -358,8 +341,8 @@ void Euler_mirrorX(double rot, double tilt, double psi,
 }
 
 /* Euler mirror XY --------------------------------------------------------- */
-void Euler_mirrorXY(double rot, double tilt, double psi,
-                    double &newrot, double &newtilt, double &newpsi)
+void Euler_mirrorXY(DOUBLE rot, DOUBLE tilt, DOUBLE psi,
+                    DOUBLE &newrot, DOUBLE &newtilt, DOUBLE &newpsi)
 {
     newrot  = rot;
     newtilt = tilt;
@@ -367,24 +350,24 @@ void Euler_mirrorXY(double rot, double tilt, double psi,
 }
 
 /* Apply a transformation matrix to Euler angles --------------------------- */
-void Euler_apply_transf(const Matrix2D<double> &L,
-                        const Matrix2D<double> &R,
-                        double rot,
-                        double tilt,
-                        double psi,
-                        double &newrot,
-                        double &newtilt,
-                        double &newpsi)
+void Euler_apply_transf(const Matrix2D<DOUBLE> &L,
+                        const Matrix2D<DOUBLE> &R,
+                        DOUBLE rot,
+                        DOUBLE tilt,
+                        DOUBLE psi,
+                        DOUBLE &newrot,
+                        DOUBLE &newtilt,
+                        DOUBLE &newpsi)
 {
 
-    Matrix2D<double> euler(3, 3), temp;
+    Matrix2D<DOUBLE> euler(3, 3), temp;
     Euler_angles2matrix(rot, tilt, psi, euler);
     temp = L * euler * R;
     Euler_matrix2angles(temp, newrot, newtilt, newpsi);
 }
 
 /* Rotate (3D) MultidimArray with 3 Euler angles ------------------------------------- */
-void Euler_rotation3DMatrix(double rot, double tilt, double psi, Matrix2D<double> &result)
+void Euler_rotation3DMatrix(DOUBLE rot, DOUBLE tilt, DOUBLE psi, Matrix2D<DOUBLE> &result)
 {
     Euler_angles2matrix(rot, tilt, psi, result, true);
 }
diff --git a/src/euler.h b/src/euler.h
index 81b08bd..28ebba1 100644
--- a/src/euler.h
+++ b/src/euler.h
@@ -66,7 +66,7 @@
  * See http://xmipp.cnb.csic.es/twiki/bin/view/Xmipp/EulerAngles for a
  * description of the Euler angles.
  */
-void Euler_angles2matrix(double a, double b, double g, Matrix2D< double >& A,
+void Euler_angles2matrix(DOUBLE a, DOUBLE b, DOUBLE g, Matrix2D< DOUBLE >& A,
                          bool homogeneous=false);
 
 /** Euler angles2direction
@@ -74,18 +74,18 @@ void Euler_angles2matrix(double a, double b, double g, Matrix2D< double >& A,
  * This function returns  a vector parallel to the  projection direction.
  * Resizes v if needed
  */
-void Euler_angles2direction(double alpha,
-						 double beta,
-						 Matrix1D< double >& v);
+void Euler_angles2direction(DOUBLE alpha,
+						 DOUBLE beta,
+						 Matrix1D< DOUBLE >& v);
 
 /** Euler direction2angles
  *
  * This function returns the 2 Euler angles (rot&tilt) associated to the direction given by
  * the vector v.
  */
-void Euler_direction2angles(Matrix1D< double >& v,
-                            double& alpha,
-                            double& beta);
+void Euler_direction2angles(Matrix1D< DOUBLE >& v,
+                            DOUBLE& alpha,
+                            DOUBLE& beta);
 
 /** "Euler" matrix --> angles
  *
@@ -101,10 +101,10 @@ void Euler_direction2angles(Matrix1D< double >& v,
  * Euler_matrix2angles(Euler, alpha, beta, gamma);
  * @endcode
  */
-void Euler_matrix2angles(const Matrix2D< double >& A,
-                         double& alpha,
-                         double& beta,
-                         double& gamma);
+void Euler_matrix2angles(const Matrix2D< DOUBLE >& A,
+                         DOUBLE& alpha,
+                         DOUBLE& beta,
+                         DOUBLE& gamma);
 
 /** Up-Down projection equivalence
  *
@@ -127,12 +127,12 @@ void Euler_matrix2angles(const Matrix2D< double >& A,
  * Euler_up_down(rot, tilt, psi, newrot, newtilt, newpsi);
  * @endcode
  */
-void Euler_up_down(double rot,
-                   double tilt,
-                   double psi,
-                   double& newrot,
-                   double& newtilt,
-                   double& newpsi);
+void Euler_up_down(DOUBLE rot,
+                   DOUBLE tilt,
+                   DOUBLE psi,
+                   DOUBLE& newrot,
+                   DOUBLE& newtilt,
+                   DOUBLE& newpsi);
 
 /** The same view but differently expressed
  *
@@ -150,12 +150,12 @@ void Euler_up_down(double rot,
  * Euler_another_set(rot, tilt, psi, newrot, newtilt, newpsi);
  * @endcode
  */
-void Euler_another_set(double rot,
-                       double tilt,
-                       double psi,
-                       double& newrot,
-                       double& newtilt,
-                       double& newpsi);
+void Euler_another_set(DOUBLE rot,
+                       DOUBLE tilt,
+                       DOUBLE psi,
+                       DOUBLE& newrot,
+                       DOUBLE& newtilt,
+                       DOUBLE& newpsi);
 
 /** Mirror over Y axis
  *
@@ -183,12 +183,12 @@ void Euler_another_set(double rot,
  * Euler_mirrorY(rot, tilt, psi, newrot, newtilt, newpsi);
  * @endcode
  */
-void Euler_mirrorY(double rot,
-                   double tilt,
-                   double psi,
-                   double& newrot,
-                   double& newtilt,
-                   double& newpsi);
+void Euler_mirrorY(DOUBLE rot,
+                   DOUBLE tilt,
+                   DOUBLE psi,
+                   DOUBLE& newrot,
+                   DOUBLE& newtilt,
+                   DOUBLE& newpsi);
 
 /** Mirror over X axis
  *
@@ -216,12 +216,12 @@ void Euler_mirrorY(double rot,
  * Euler_mirrorX(rot, tilt, psi, newrot, newtilt, newpsi);
  * @endcode
  */
-void Euler_mirrorX(double rot,
-                   double tilt,
-                   double psi,
-                   double& newrot,
-                   double& newtilt,
-                   double& newpsi);
+void Euler_mirrorX(DOUBLE rot,
+                   DOUBLE tilt,
+                   DOUBLE psi,
+                   DOUBLE& newrot,
+                   DOUBLE& newtilt,
+                   DOUBLE& newpsi);
 
 /** Mirror over X and Y axes
  *
@@ -250,12 +250,12 @@ void Euler_mirrorX(double rot,
  * Euler_mirrorX(rot, tilt, psi, newrot, newtilt, newpsi);
  * @endcode
  */
-void Euler_mirrorXY(double rot,
-                    double tilt,
-                    double psi,
-                    double& newrot,
-                    double& newtilt,
-                    double& newpsi);
+void Euler_mirrorXY(DOUBLE rot,
+                    DOUBLE tilt,
+                    DOUBLE psi,
+                    DOUBLE& newrot,
+                    DOUBLE& newtilt,
+                    DOUBLE& newpsi);
 
 /** Apply a geometrical transformation
  *
@@ -277,21 +277,21 @@ void Euler_mirrorXY(double rot,
  * system.
  *
  * @code
- * Matrix2D< double > R60 = rotation3DMatrix(60, 'Z');
+ * Matrix2D< DOUBLE > R60 = rotation3DMatrix(60, 'Z');
  * R60.resize(3, 3); // Get rid of homogeneous part
- * Matrix2D< double > I(3, 3);
+ * Matrix2D< DOUBLE > I(3, 3);
  * I.initIdentity();
  * Euler_apply_transf(I, R60, rot, tilt, psi, newrot, newtilt, newpsi);
  * @endcode
  */
-void Euler_apply_transf(const Matrix2D< double >& L,
-                        const Matrix2D< double >& R,
-                        double rot,
-                        double tilt,
-                        double psi,
-                        double& newrot,
-                        double& newtilt,
-                        double& newpsi);
+void Euler_apply_transf(const Matrix2D< DOUBLE >& L,
+                        const Matrix2D< DOUBLE >& R,
+                        DOUBLE rot,
+                        DOUBLE tilt,
+                        DOUBLE psi,
+                        DOUBLE& newrot,
+                        DOUBLE& newtilt,
+                        DOUBLE& newpsi);
 
 /** 3D Rotation matrix after 3 Euler angles
  *
@@ -303,8 +303,8 @@ void Euler_apply_transf(const Matrix2D< double >& L,
  * Matrix2D< float > euler = Euler_rotation3DMatrix(60, 30, 60);
  * @endcode
  */
-void Euler_rotation3DMatrix(double rot, double tilt, double psi,
-                            Matrix2D<double> &result);
+void Euler_rotation3DMatrix(DOUBLE rot, DOUBLE tilt, DOUBLE psi,
+                            Matrix2D<DOUBLE> &result);
 
 //@}
 
diff --git a/src/exp_model.cpp b/src/exp_model.cpp
index dd61407..84c002f 100644
--- a/src/exp_model.cpp
+++ b/src/exp_model.cpp
@@ -28,16 +28,6 @@ void ExpOriginalParticle::addParticle(long int _particle_id, int _random_subset,
 	particles_order.push_back(_order);
 }
 
-	long int Experiment::numberOfImages(int random_subset)
-{
-	long int result = 0;
-	for (long int i = 0; i < particles.size(); i++)
-		if (random_subset == 0 || particles[i].random_subset == random_subset)
-			result += particles[i].images.size();
-
-	return result;
-}
-
 long int Experiment::numberOfParticles(int random_subset)
 {
 	if (random_subset == 0)
@@ -79,9 +69,8 @@ long int Experiment::numberOfGroups()
 	return groups.size();
 }
 
-int Experiment::getNrImagesInSeries(long int part_id)
+long int Experiment::getMicrographId(long int part_id)
 {
-//#define DEBUG_CHECKSIZES
 #ifdef DEBUG_CHECKSIZES
 	if (part_id >= particles.size())
 	{
@@ -89,10 +78,10 @@ int Experiment::getNrImagesInSeries(long int part_id)
 		REPORT_ERROR("part_id >= particles.size()");
 	}
 #endif
-	return (particles[part_id].images).size();
+	return (particles[part_id]).micrograph_id;
 }
 
-long int Experiment::getMicrographId(long int part_id, int inseries_no)
+long int Experiment::getGroupId(long int part_id)
 {
 #ifdef DEBUG_CHECKSIZES
 	if (part_id >= particles.size())
@@ -100,30 +89,8 @@ long int Experiment::getMicrographId(long int part_id, int inseries_no)
 		std::cerr<< "part_id= "<<part_id<<" particles.size()= "<< particles.size() <<std::endl;
 		REPORT_ERROR("part_id >= particles.size()");
 	}
-	if (inseries_no >= particles[part_id].images.size())
-	{
-		std::cerr<< "inseries_no= "<<inseries_no<<" particles[part_id].images.size()= "<< particles[part_id].images.size() <<std::endl;
-		REPORT_ERROR("inseries_no >= particles[part_id].images.size()]");
-	}
 #endif
-	return (particles[part_id].images[inseries_no]).micrograph_id;
-}
-
-long int Experiment::getGroupId(long int part_id, int inseries_no)
-{
-#ifdef DEBUG_CHECKSIZES
-	if (part_id >= particles.size())
-	{
-		std::cerr<< "part_id= "<<part_id<<" particles.size()= "<< particles.size() <<std::endl;
-		REPORT_ERROR("part_id >= particles.size()");
-	}
-	if (inseries_no >= particles[part_id].images.size())
-	{
-		std::cerr<< "inseries_no= "<<inseries_no<<" particles[part_id].images.size()= "<< particles[part_id].images.size() <<std::endl;
-		REPORT_ERROR("inseries_no >= particles[part_id].images.size()]");
-	}
-#endif
-	return (particles[part_id].images[inseries_no]).group_id;
+	return (particles[part_id]).group_id;
 }
 
 int Experiment::getRandomSubset(long int part_id)
@@ -131,97 +98,15 @@ int Experiment::getRandomSubset(long int part_id)
 	return particles[part_id].random_subset;
 }
 
-long int Experiment::getImageId(long int part_id, int inseries_no)
-{
-	return (particles[part_id].images[inseries_no]).id;
-}
-
-MetaDataTable Experiment::getMetaDataImage(long int part_id, int inseries_no)
+MetaDataTable Experiment::getMetaDataImage(long int part_id)
 {
 	MetaDataTable result;
-	long int img_id = getImageId(part_id, inseries_no);
-	result.addObject(MDimg.getObject(img_id));
+	result.addObject(MDimg.getObject(part_id));
 	return result;
 }
 
-MetaDataTable Experiment::getMetaDataMicrograph(long int part_id, int inseries_no)
-{
-	MetaDataTable result;
-	long int mic_id = getMicrographId(part_id, inseries_no);
-	result.addObject(MDmic.getObject(mic_id));
-	return result;
-}
-
-Matrix2D<double> Experiment::getMicrographTransformationMatrix(long int micrograph_id)
-{
-
-	Matrix2D<double> R(3,3);
-
-	if (MDmic.containsLabel(EMDL_MATRIX_1_1))
-	{
-
-#ifdef DEBUG_CHECKSIZES
-	if (micrograph_id >= MDmic.numberOfObjects())
-	{
-		std::cerr<< "micrograph_id= "<<micrograph_id<<" MDmic.lastObject()= "<< MDmic.lastObject() <<std::endl;
-		REPORT_ERROR("micrograph_id > MDmic.lastObject()");
-	}
-#endif
-		//TODO: default values for getValue
-		MDmic.getValue(EMDL_MATRIX_1_1, R(0,0), micrograph_id);
-		MDmic.getValue(EMDL_MATRIX_1_2, R(0,1), micrograph_id);
-		MDmic.getValue(EMDL_MATRIX_1_3, R(0,2), micrograph_id);
-		MDmic.getValue(EMDL_MATRIX_2_1, R(1,0), micrograph_id);
-		MDmic.getValue(EMDL_MATRIX_2_2, R(1,1), micrograph_id);
-		MDmic.getValue(EMDL_MATRIX_2_3, R(1,2), micrograph_id);
-		MDmic.getValue(EMDL_MATRIX_3_1, R(2,0), micrograph_id);
-		MDmic.getValue(EMDL_MATRIX_3_2, R(2,1), micrograph_id);
-		MDmic.getValue(EMDL_MATRIX_3_3, R(2,2), micrograph_id);
-	}
-	else if (MDmic.containsLabel(EMDL_MICROGRAPH_TILT_ANGLE))
-	{
-#ifdef DEBUG_CHECKSIZES
-	if (micrograph_id >= MDmic.numberOfObjects())
-	{
-		std::cerr<< "micrograph_id= "<<micrograph_id<<" MDmic.lastObject()= "<< MDmic.lastObject() <<std::endl;
-		REPORT_ERROR("micrograph_id > MDmic.lastObject()");
-	}
-#endif
-
-	double tiltdir, tiltangle, outofplane;
-		MDmic.getValue(EMDL_MICROGRAPH_TILT_ANGLE, tiltangle, micrograph_id);
-		// By default tiltdir = 0
-		if (!MDmic.getValue(EMDL_MICROGRAPH_TILT_AXIS_DIRECTION, tiltdir, micrograph_id))
-			tiltdir = 0.;
-		// By default in-plane tilt axis....
-		if (!MDmic.getValue(EMDL_MICROGRAPH_TILT_AXIS_OUTOFPLANE, outofplane, micrograph_id))
-			outofplane= 90.;
-
-		// Transformation matrix
-		// Get the direction of the tilt axis as a Matrix1D
-		Matrix1D<double> dir_axis(3);
-		Euler_angles2direction(tiltdir, outofplane, dir_axis);
-		// Calculate the corresponding 3D rotation matrix
-		rotation3DMatrix(tiltangle, dir_axis, R, false);
-		// Somehow have to take the inverse of that...
-		R = R.inv();
-	}
-	else
-	{
-		R.initIdentity();
-	}
-
-	return R;
-}
-
-Matrix2D<double> Experiment::getMicrographTransformationMatrix(long int part_id, int inseries_no)
-{
-	long int mic_id = getMicrographId(part_id, inseries_no);
-	return getMicrographTransformationMatrix(mic_id);
-}
-
-
-long int Experiment::addImage(long int group_id, long int micrograph_id, long int particle_id)
+long int Experiment::addParticle(long int group_id,
+		long int micrograph_id, int random_subset)
 {
 
 	if (group_id >= groups.size())
@@ -230,36 +115,14 @@ long int Experiment::addImage(long int group_id, long int micrograph_id, long in
 	if (micrograph_id >= micrographs.size())
 		REPORT_ERROR("Experiment::addImage: micrograph_id out of range");
 
-	if (particle_id >= particles.size())
-	{
-		std::cerr << " particle_id= " << particle_id << " particles.size()= " << particles.size() << std::endl;
-		REPORT_ERROR("Experiment::addImage: particle_id out of range");
-	}
-
-	ExpImage image;
-	image.group_id = group_id;
-	image.micrograph_id = micrograph_id;
-	image.particle_id = particle_id;
-	// Add new entry in the MDimg MetaDataTable of this Experiment, and get image.id
-	image.id = MDimg.addObject();
-
-	// add this ExpImage to the ExpParticle and the ExpMicrograph
-	(micrographs[micrograph_id].images).push_back(image);
-	(particles[particle_id].images).push_back(image);
-
-	return image.id;
-
-}
-
-long int Experiment::addParticle(std::string part_name, int random_subset)
-{
-
 	ExpParticle particle;
 	particle.id = particles.size();
+	particle.group_id = group_id;
+	particle.micrograph_id = micrograph_id;
 	particle.random_subset = random_subset;
-	particle.name = part_name;
 	// Push back this particle in the particles vector
 	particles.push_back(particle);
+	(micrographs[micrograph_id].particle_ids).push_back(particle.id);
 
 	// Return the id in the particles vector
 	return particle.id;
@@ -282,7 +145,7 @@ long int Experiment::addOriginalParticle(std::string part_name, int _random_subs
 
 long int Experiment::addGroup(std::string group_name)
 {
-	// Add new entry in the MDmic MetaDataTable of this Experiment
+	// Add new group to this Experiment
 	ExpGroup group;
 	group.id = groups.size(); // start counting groups at 0!
 	group.name = group_name;
@@ -297,7 +160,7 @@ long int Experiment::addGroup(std::string group_name)
 
 long int Experiment::addAverageMicrograph(std::string avg_mic_name)
 {
-	// Add new entry in the MDmic MetaDataTable of this Experiment
+	// Add new averagemicrograph to this Experiment
 	AverageMicrograph micrograph;
 	micrograph.id = average_micrographs.size();
 	micrograph.name = avg_mic_name;
@@ -312,9 +175,9 @@ long int Experiment::addAverageMicrograph(std::string avg_mic_name)
 
 long int Experiment::addMicrograph(std::string mic_name)
 {
-	// Add new entry in the MDmic MetaDataTable of this Experiment
+	// Add new micrograph to this Experiment
 	ExpMicrograph micrograph;
-	micrograph.id = MDmic.addObject();
+	micrograph.id = micrographs.size();
 	micrograph.name = mic_name;
 
 	// Push back this micrograph
@@ -372,17 +235,12 @@ void Experiment::divideOriginalParticlesInRandomHalves(int seed)
 				REPORT_ERROR("ERROR Experiment::divideParticlesInRandomHalves: invalid number for random subset (i.e. not 1 or 2): " + integerToString(random_subset));
 
 			// Loop over all particles in each ori_particle and set their random_subset
-			// Also set the EMDL_PARTICLE_RANDOM_SUBSET in the MDimg of all images for this particle
 			for (long int j = 0; j < ori_particles[i].particles_id.size(); j++)
 			{
 				long int part_id = (ori_particles[i]).particles_id[j];
 				{
 					particles[part_id].random_subset = random_subset;
-					for (long int n = 0; n < (particles[part_id]).images.size(); n++)
-					{
-						long int img_id = ((particles[part_id]).images[n]).id;
-						MDimg.setValue(EMDL_PARTICLE_RANDOM_SUBSET, random_subset, img_id);
-					}
+					MDimg.setValue(EMDL_PARTICLE_RANDOM_SUBSET, random_subset, part_id);
 				}
 			}
 		}
@@ -456,42 +314,58 @@ void Experiment::randomiseOriginalParticlesOrder(int seed, bool do_split_random_
 	}
 }
 
-int Experiment::maxNumberOfImagesPerOriginalParticle(long int first_ori_particle_id, long int last_ori_particle_id)
-{
-
-	// By default search all particles
-	if (first_ori_particle_id < 0)
-		first_ori_particle_id = 0;
-	if (last_ori_particle_id < 0)
-		last_ori_particle_id = ori_particles.size() - 1;
 
-	int result = 0;
-	for (long int i = first_ori_particle_id; i <= last_ori_particle_id; i++)
-	{
-		// Loop over all particles in this ori_particle
-		for (int j = 0; j < ori_particles[i].particles_id.size(); j++)
-		{
-			long int part_id = ori_particles[i].particles_id[j];
-			int val = getNrImagesInSeries(part_id);
-			if (val > result)
-				result = val;
-
-		}
-	}
-	return result;
-
-}
-void Experiment::expandToMovieFrames(FileName fn_data_movie)
+void Experiment::expandToMovieFrames(FileName fn_data_movie, int verb)
 {
 
+//#define DEBUG_EXPAND
+#ifdef DEBUG_EXPAND
+	Timer timer;
+	int tall = timer.setNew("ALL");
+	int tread = timer.setNew("read");
+	int tsort = timer.setNew("sort");
+	int tmakevec = timer.setNew("makevec");
+	int tselect = timer.setNew("select from MDimg");
+	int tsearchmic = timer.setNew("seacrh mic");
+	int tsearchgroup = timer.setNew("seacrh group");
+	int tsearchori = timer.setNew("seacrh oriparticle");
+	int taddpart = timer.setNew("add particle");
+	int torderori = timer.setNew("order particles in oriparticle");
+	timer.tic(tall);
+#endif
+
 	MetaDataTable MDmovie;
+#ifdef DEBUG_EXPAND
+	timer.tic(tread);
+#endif
 	MDmovie.read(fn_data_movie);
-	if (!MDmovie.containsLabel(EMDL_MICROGRAPH_NAME) || !MDmovie.containsLabel(EMDL_PARTICLE_NAME))
-		REPORT_ERROR("Experiment::expandToMovieFrames Error: movie metadata file does not contain rlnMicrographName as well as rlnParticleName");
+
+#ifdef DEBUG_EXPAND
+	std::cerr << "now read in MDmovie" << std::endl;
+	timer.toc(tread);
+#endif
+	if (!MDmovie.containsLabel(EMDL_MICROGRAPH_NAME) || !MDmovie.containsLabel(EMDL_PARTICLE_ORI_NAME))
+		REPORT_ERROR("Experiment::expandToMovieFrames Error: movie metadata file does not contain rlnMicrographName as well as rlnOriginalParticleName");
+
+	// Sort movie STAR file on EMDL_MICROGRAPH_NAME
+#ifdef DEBUG_EXPAND
+	timer.tic(tsort);
+#endif
+	MDmovie.newSort(EMDL_MICROGRAPH_NAME, false, true); // false=no reverse, true= do sort only on string after "@"
+#ifdef DEBUG_EXPAND
+	//MDmovie.write("sorted_movie.star");
+	//MDimg.write("sorted_MDimg.star");
+	//std::cerr << "Written sorted_movie.star and sorted_MDimg.star" << std::endl;
+        std::cerr << "now sorted MDmovie" << std::endl;
+	timer.toc(tsort);
+#endif
 
 	// Re-build new Experiment Exp_movie from scratch
 	Experiment Exp_movie;
 
+#ifdef DEBUG_EXPAND
+	timer.tic(tmakevec);
+#endif
 	// Make a temporary vector of all image names in the current Experiment to gain speed
 	std::vector<FileName> fn_curr_imgs, fn_curr_groups;
 	std::vector<int> count_frames;
@@ -506,21 +380,42 @@ void Experiment::expandToMovieFrames(FileName fn_data_movie)
 		fn_curr_groups.push_back(groups[group_id-1].name);
 		count_frames.push_back(0);
 	}
+#ifdef DEBUG_EXPAND
+	timer.toc(tmakevec);
+#endif
 
+	if (verb > 0)
+		init_progress_bar(MDmovie.numberOfObjects());
+	int bar_step = MDmovie.numberOfObjects() / 60;
+
+	FileName last_found_fn_mic="";
+	long int last_found_mic_idx = -1, last2_found_mic_idx = -1;
+	long i_object = 0;
 	FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDmovie)
 	{
-		long int group_id, mic_id, part_id, image_id;
+		long int group_id, mic_id, part_id;
 		int my_random_subset, my_class;
-		double rot, tilt, psi, xoff, yoff;
-		FileName fn_movie_part, fn_curr_img, group_name;
-		MDmovie.getValue(EMDL_PARTICLE_NAME, fn_movie_part);
+		DOUBLE rot, tilt, psi, xoff, yoff;
+		FileName fn_curr_img, group_name, fn_ori_part, fn_mic;
+		MDmovie.getValue(EMDL_PARTICLE_ORI_NAME, fn_ori_part);
 
 		bool have_found = false;
-		for (long int idx = 0; idx < fn_curr_imgs.size(); idx++)
+		// Find this particle in the current Experiment
+		// Start searching from the beginning of the last found micrograph (because both MDimg and MDmovie are sorted on that)
+		// Particles are not necessarily ordered in the micrographs, so therefore start searching at first idx of each new micrograph!
+
+#ifdef DEBUG_EXPAND
+		timer.tic(tselect);
+#endif
+		// Not starting at the beginning every time makes this scale ~linearly, instead of squared! :-)
+		// rlnMicrographName needs to be sorted in both MDimg and MDmovie for this to work (which is now the case)
+		//for (long int idx = 0; idx < fn_curr_imgs.size(); idx++)
+		for (long int idx = last2_found_mic_idx + 1; idx < fn_curr_imgs.size(); idx++)
 		{
-			// Found a match
-			if (fn_curr_imgs[idx] == fn_movie_part)
+			// Find a match
+			if (fn_curr_imgs[idx] == fn_ori_part)
 			{
+
 				// Now get the angles from the current Experiment
 				MDimg.getValue(EMDL_ORIENT_ROT, rot, idx);
 				MDimg.getValue(EMDL_ORIENT_TILT, tilt, idx);
@@ -531,6 +426,13 @@ void Experiment::expandToMovieFrames(FileName fn_data_movie)
 				// Also get the random subset (if present)
 				if (!MDimg.getValue(EMDL_PARTICLE_RANDOM_SUBSET, my_random_subset, idx))
 					my_random_subset = 0;
+				MDimg.getValue(EMDL_MICROGRAPH_NAME, fn_mic, idx);
+				if (last_found_fn_mic != fn_mic)
+				{
+					last_found_fn_mic = fn_mic;
+					last2_found_mic_idx = last_found_mic_idx;
+					last_found_mic_idx = idx;
+				}
 
 				// count how many frames are measured for each particle
 				count_frames[idx]++;
@@ -542,6 +444,9 @@ void Experiment::expandToMovieFrames(FileName fn_data_movie)
 			}
 
 		}
+#ifdef DEBUG_EXPAND
+		timer.toc(tselect);
+#endif
 
 		// Only include particles that were already in the current Experiment
 		if (have_found)
@@ -551,8 +456,11 @@ void Experiment::expandToMovieFrames(FileName fn_data_movie)
 			MDmovie.getValue(EMDL_MICROGRAPH_NAME, mic_name);
 
 			// If this micrograph did not exist in the Exp_movie yet, add it to the Exp_movie experiment
+#ifdef DEBUG_EXPAND
+		timer.tic(tsearchmic);
+#endif
 			mic_id = -1;
-			for (long int i = 0; i < Exp_movie.micrographs.size(); i++)
+			for (long int i = Exp_movie.micrographs.size() - 1; i >= 0; i--)
 			{
 				if (Exp_movie.micrographs[i].name == mic_name)
 				{
@@ -562,18 +470,19 @@ void Experiment::expandToMovieFrames(FileName fn_data_movie)
 			}
 			if (mic_id < 0)
 				mic_id = Exp_movie.addMicrograph(mic_name);
-
+#ifdef DEBUG_EXPAND
+		timer.toc(tsearchmic);
+#endif
 
 			// Add frameno@ to existing group names, so that separate weighting may be applied to different dose images
 			// NO THIS HAS NO SENSE IF WE'RE ONLY DOING ONE ITERATION ANYWAY!!! THEN IT'S JUST A WASTE OF MEMORY....
-			//std::string dum;
-			//long int frameno;
-			//mic_name.decompose(frameno, dum);
-			//group_name.compose(frameno, group_name, 4);
 
+#ifdef DEBUG_EXPAND
+		timer.tic(tsearchgroup);
+#endif
 			// If this group did not exist yet, add it to the experiment
 			group_id = -1;
-			for (long int i = 0; i < Exp_movie.groups.size(); i++)
+			for (long int i = Exp_movie.groups.size() - 1; i >= 0; i--)
 			{
 				if (Exp_movie.groups[i].name == group_name)
 				{
@@ -583,48 +492,51 @@ void Experiment::expandToMovieFrames(FileName fn_data_movie)
 			}
 			if (group_id < 0)
 				group_id = Exp_movie.addGroup(group_name);
+#ifdef DEBUG_EXPAND
+		timer.toc(tsearchgroup);
+#endif
 
+#ifdef DEBUG_EXPAND
+		timer.tic(taddpart);
+#endif
 			// Create a new particle
-			std::string part_name;
-			part_name= integerToString( Exp_movie.particles.size() + 1); // start counting at 1
-			part_id = Exp_movie.addParticle(part_name, my_random_subset);
+			part_id = Exp_movie.addParticle(group_id, mic_id, my_random_subset);
+                        Exp_movie.MDimg.addObject();
 
-			image_id = Exp_movie.addImage(group_id, mic_id, part_id);
 			// Copy the current row of MDimgin into the current row of MDimg
-			Exp_movie.MDimg.setObject(MDmovie.getObject(), image_id);
+			Exp_movie.MDimg.setObject(MDmovie.getObject(), part_id);
 
 			// Set the orientations
-			Exp_movie.MDimg.setValue(EMDL_ORIENT_ROT, rot, image_id);
-			Exp_movie.MDimg.setValue(EMDL_ORIENT_TILT, tilt, image_id);
-			Exp_movie.MDimg.setValue(EMDL_ORIENT_PSI, psi, image_id);
-			Exp_movie.MDimg.setValue(EMDL_ORIENT_ORIGIN_X, xoff, image_id);
-			Exp_movie.MDimg.setValue(EMDL_ORIENT_ORIGIN_Y, yoff, image_id);
+			Exp_movie.MDimg.setValue(EMDL_ORIENT_ROT, rot, part_id);
+			Exp_movie.MDimg.setValue(EMDL_ORIENT_TILT, tilt, part_id);
+			Exp_movie.MDimg.setValue(EMDL_ORIENT_PSI, psi, part_id);
+			Exp_movie.MDimg.setValue(EMDL_ORIENT_ORIGIN_X, xoff, part_id);
+			Exp_movie.MDimg.setValue(EMDL_ORIENT_ORIGIN_Y, yoff, part_id);
 			// Now also set the priors on the orientations equal to the orientations from the averages
-			Exp_movie.MDimg.setValue(EMDL_ORIENT_ROT_PRIOR, rot, image_id);
-			Exp_movie.MDimg.setValue(EMDL_ORIENT_TILT_PRIOR, tilt, image_id);
-			Exp_movie.MDimg.setValue(EMDL_ORIENT_PSI_PRIOR, psi, image_id);
-			Exp_movie.MDimg.setValue(EMDL_ORIENT_ORIGIN_X_PRIOR, xoff, image_id);
-			Exp_movie.MDimg.setValue(EMDL_ORIENT_ORIGIN_Y_PRIOR, yoff, image_id);
-			Exp_movie.MDimg.setValue(EMDL_PARTICLE_CLASS, my_class, image_id);
-			Exp_movie.MDimg.setValue(EMDL_PARTICLE_RANDOM_SUBSET, my_random_subset, image_id);
+			Exp_movie.MDimg.setValue(EMDL_ORIENT_ROT_PRIOR, rot, part_id);
+			Exp_movie.MDimg.setValue(EMDL_ORIENT_TILT_PRIOR, tilt, part_id);
+			Exp_movie.MDimg.setValue(EMDL_ORIENT_PSI_PRIOR, psi, part_id);
+			Exp_movie.MDimg.setValue(EMDL_ORIENT_ORIGIN_X_PRIOR, xoff, part_id);
+			Exp_movie.MDimg.setValue(EMDL_ORIENT_ORIGIN_Y_PRIOR, yoff, part_id);
+			Exp_movie.MDimg.setValue(EMDL_PARTICLE_CLASS, my_class, part_id);
+			Exp_movie.MDimg.setValue(EMDL_PARTICLE_RANDOM_SUBSET, my_random_subset, part_id);
 			// Set normcorrection to 1
-			double norm = 1.;
+			DOUBLE norm = 1.;
 			Exp_movie.MDimg.setValue(EMDL_IMAGE_NORM_CORRECTION, norm);
-
-			// Get the rlnParticleName and set this into rlnOriginalParticleName to prevent re-reading of this file to be handled differently..
-			FileName fn_ori_part;
-			Exp_movie.MDimg.getValue(EMDL_PARTICLE_NAME, fn_ori_part, image_id);
-			Exp_movie.MDimg.setValue(EMDL_PARTICLE_ORI_NAME, fn_ori_part, image_id);
-			// Set the particle number in its new rlnParticleName
-			Exp_movie.MDimg.setValue(EMDL_PARTICLE_NAME, part_name, image_id);
 			// Set the new group name
 			Exp_movie.MDimg.setValue(EMDL_MLMODEL_GROUP_NAME, group_name);
+#ifdef DEBUG_EXPAND
+		timer.toc(taddpart);
+#endif
 
-
+#ifdef DEBUG_EXPAND
+		timer.tic(tsearchori);
+#endif
 			// Add ExpOriParticles
 			// If this ori_particle did not exist in the Exp_movie yet, add it to the Exp_movie experiment
 			long int ori_part_id = -1;
-			for (long int i = 0; i < Exp_movie.ori_particles.size(); i++)
+			// Loop backward to find the previous ori_particle first
+			for (long int i = Exp_movie.ori_particles.size() - 1; i >= 0; i--)
 			{
 				if (Exp_movie.ori_particles[i].name == fn_ori_part)
 				{
@@ -635,6 +547,11 @@ void Experiment::expandToMovieFrames(FileName fn_data_movie)
 			// If no ExpOriParticles with this name was found, then add new one
 			if (ori_part_id < 0)
 				ori_part_id = Exp_movie.addOriginalParticle(fn_ori_part, my_random_subset);
+#ifdef DEBUG_EXPAND
+		timer.toc(tsearchori);
+#endif
+
+
 			// Add this particle to the OriginalParticle
 			// get Number from mic_name (-1 if empty mic_name, or no @ in mic_name)
 			std::string fnt;
@@ -643,8 +560,15 @@ void Experiment::expandToMovieFrames(FileName fn_data_movie)
 			(Exp_movie.ori_particles[ori_part_id]).addParticle(part_id, my_random_subset, my_order);
 
 		}
+
+		if (verb > 0 && i_object % bar_step == 0)
+			progress_bar(i_object);
+		i_object++;
 	}
 
+	if (verb > 0)
+		progress_bar(MDmovie.numberOfObjects());
+
 	if (Exp_movie.MDimg.numberOfObjects() == 0)
 		REPORT_ERROR("Experiment::expandToMovieFrames: ERROR: no movie frames selected. Check filenames of micrographs, movies and particle stacks!");
 
@@ -657,8 +581,19 @@ void Experiment::expandToMovieFrames(FileName fn_data_movie)
 	// Now replace the current Experiment with Exp_movie
 	(*this) = Exp_movie;
 
+#ifdef DEBUG_EXPAND
+		timer.tic(torderori);
+#endif
 	// Order the particles in each ori_particle
 	orderParticlesInOriginalParticles();
+#ifdef DEBUG_EXPAND
+		timer.toc(torderori);
+#endif
+
+#ifdef DEBUG_EXPAND
+	timer.toc(tall);
+	timer.printTimes(true);
+#endif
 
 
 }
@@ -690,47 +625,6 @@ void Experiment::orderParticlesInOriginalParticles()
 
 }
 
-void Experiment::getAverageMicrographs()
-{
-
-	// Loop over all ori_particles and group in identical AverageMicrographs
-	// Recognize identical AverageMicrographs, by common MicrographNames AFTER the "@" sign
-	// This will only work for movie-processing, where the rlnMicrographName indeed contains an "@"
-
-	average_micrographs.clear();
-	for (long int i = 0; i < ori_particles.size(); i++)
-	{
-		// Get the first particle (i.e. movie-frame) from this original_particle (i.e. movie)
-		long int part_id = ori_particles[i].particles_id[0];
-		// The micrograph_id from the first image of the first particle (movie-particles only have 1 image/particle!!!!)
-		// This whole series-stuff is not turning out to be extremely useful yet....
-		long int mic_id = getMicrographId(part_id, 0);
-		FileName mic_name = micrographs[mic_id].name;
-		std::string avg_mic_name;
-		long int frame_nr;
-		// Get the part AFTER the "@" sign
-		mic_name.decompose(frame_nr, avg_mic_name);
-		// If this micrograph did not exist yet, add it to the experiment
-		long int avg_mic_id = -1;
-		for (long int ii = average_micrographs.size() - 1; ii >= 0; ii--) // search backwards to find match faster
-		{
-			if (average_micrographs[ii].name == avg_mic_name)
-			{
-				avg_mic_id = micrographs[ii].id;
-				break;
-			}
-		}
-		if (avg_mic_id < 0)
-		{
-			avg_mic_id = addAverageMicrograph(avg_mic_name);
-		}
-
-		// Add this OriginalParticle to the AverageMicrograph
-		average_micrographs[avg_mic_id].ori_particles_id.push_back(i);
-	}
-
-}
-
 void Experiment::usage()
 {
 	std::cout
@@ -739,19 +633,29 @@ void Experiment::usage()
 }
 
 // Read from file
-void Experiment::read(FileName fn_exp, bool do_ignore_particle_name)
+void Experiment::read(FileName fn_exp, bool do_ignore_original_particle_name, bool do_ignore_group_name, bool do_preread_images)
 {
 
 //#define DEBUG_READ
 #ifdef DEBUG_READ
 	std::cerr << "Entering Experiment::read" << std::endl;
+	Timer timer;
+	int tall = timer.setNew("ALL");
+	int tread = timer.setNew("read");
+	int tsort = timer.setNew("sort");
+	int tfill = timer.setNew("fill");
+	int tgroup = timer.setNew("find group");
+	int tori = timer.setNew("find ori_particle");
+	int tdef = timer.setNew("set defaults");
+	int tend = timer.setNew("ending");
 	char c;
+	timer.tic(tall);
+	timer.tic(tread);
 #endif
 
 	// Initialize by emptying everything
 	clear();
-	MetaDataTable MDmicin, MDimgin;
-	long int group_id, mic_id, part_id, image_id;
+	long int group_id, mic_id, part_id;
 
 	if (!fn_exp.isStarFile())
 	{
@@ -765,122 +669,193 @@ void Experiment::read(FileName fn_exp, bool do_ignore_particle_name)
 			REPORT_ERROR("Experiment::read: ERROR: MRC stacks of 2D images should be have extension .mrcs, not .mrc!");
 
 		// Read in header-only information to get the NSIZE of the stack
-		Image<double> img;
+		Image<DOUBLE> img;
 		img.read(fn_exp, false); // false means skip data, only read header
-
+		
+		// allocate 1 block of memory
+		particles.reserve(NSIZE(img()));
+		ori_particles.reserve(NSIZE(img()));
 		for (long int n = 0; n <  NSIZE(img()); n++)
 		{
 			FileName fn_img;
 			fn_img.compose(n+1, fn_exp); // fn_img = integerToString(n) + "@" + fn_exp;
 			// Add the particle to my_area = 0
-			part_id = addParticle("particle");
-			// Add this image to the area
-			image_id = addImage(group_id, mic_id, part_id);
+			part_id = addParticle(group_id, mic_id);
+                        MDimg.addObject();
+			if (do_preread_images)
+			{
+				Image<DOUBLE> img;
+				img.read(fn_img);
+				img().setXmippOrigin();
+				particles[part_id].img = img();
+			}
 			// Also add OriginalParticle
 			(ori_particles[addOriginalParticle("particle")]).addParticle(part_id, 0, -1);
-
 			// Set the filename and other metadata parameters
-			MDimg.setValue(EMDL_IMAGE_NAME, fn_img, image_id);
+			MDimg.setValue(EMDL_IMAGE_NAME, fn_img, part_id);
 		}
 
 	}
 	else
 	{
-		// Read all metadata from a STAR file
-		bool contains_images_block;
-
-		// First try reading a data_images block into MDimgin (as written by Experiment::write() )
-		MDimgin.read(fn_exp, "images");
-		// If that did not work, try reading the first data-block in the file
-		if (MDimgin.isEmpty())
-		{
-			MDimgin.read(fn_exp);
-			contains_images_block = false;
-		}
-		else
-		{
-			contains_images_block = true;
-		}
+		// Just read first data block
+		MDimg.read(fn_exp);
 
 #ifdef DEBUG_READ
-	std::cerr << "Done reading MDimgin" << std::endl;
-	std::cerr << "Press any key to continue..." << std::endl;
-	std::cin >> c;
+	std::cerr << "Done reading MDimg" << std::endl;
+	timer.toc(tread);
+	timer.tic(tsort);
+	//std::cerr << "Press any key to continue..." << std::endl;
+	//std::cin >> c;
 #endif
 
-		// If there is no EMDL_MICROGRAPH_NAME, then just use a single group and micrograph
-		if (!MDimgin.containsLabel(EMDL_MICROGRAPH_NAME))
+		// Sort input particles on micrographname
+		bool is_mic_a_movie = false, star_contains_micname;
+		star_contains_micname = MDimg.containsLabel(EMDL_MICROGRAPH_NAME);
+		if (star_contains_micname)
 		{
+			// See if the micrograph names contain an "@", i.e. whether they are movies and we are inside polishing or so.
+			FileName fn_mic;
+			MDimg.getValue(EMDL_MICROGRAPH_NAME, fn_mic);
+			if (fn_mic.contains("@"))
+			{
+				is_mic_a_movie = true;
+				MDimg.newSort(EMDL_MICROGRAPH_NAME, false, true); // sort on part AFTER "@"
+			}
+			else
+			{
+				is_mic_a_movie = false;
+				MDimg.newSort(EMDL_MICROGRAPH_NAME); // just sort on fn_mic
+			}
+
+			if (do_ignore_group_name)
+				group_id = addGroup("group");
+		}
+		else
+		{
+			// If there is no EMDL_MICROGRAPH_NAME, then just use a single group and micrograph
 			group_id = addGroup("group");
 			mic_id = addMicrograph("micrograph");
 		}
-
-		// Now Loop over all objects in the metadata file and fill the logical tree of the experiment
-#ifdef DEBUG_READ
-		std::cerr << " sizeof(int)= " << sizeof(int) << std::endl;
-		std::cerr << " sizeof(long int)= " << sizeof(long int) << std::endl;
-		std::cerr << " sizeof(double)= " << sizeof(double) << std::endl;
-		std::cerr << " sizeof(ExpImage)= " << sizeof(ExpImage) << std::endl;
-		std::cerr << " sizeof(ExpParticle)= " << sizeof(ExpParticle) << std::endl;
-		std::cerr << " sizeof(ExpMicrograph)= " << sizeof(ExpMicrograph) << std::endl;
-		std::cerr << " sizeof(std::vector<long int>)= " << sizeof(std::vector<long int>) << std::endl;
-		long int nr_read = 0;
-
-#endif
-		// Reserve the same number of particles as there are images in MDimgin
-		particles.reserve(MDimgin.size() * (sizeof(ExpParticle) + sizeof(ExpImage)));
-		// TODO precalculate this somehow?! ARE THESE RESERVES NECESSARY ANYWAY???!!!
-		micrographs.reserve(4000);
-
 #ifdef DEBUG_READ
-		std::cerr << "Done reserving" << std::endl;
-		std::cerr << "Press any key to continue..." << std::endl;
-		std::cin >> c;
+	std::cerr << "Done sorting MDimg" << std::endl;
+	timer.toc(tsort);
+	timer.tic(tfill);
+	long nr_read = 0;
 #endif
-
-		FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDimgin)
+                // allocate 1 block of memory
+                particles.reserve(MDimg.numberOfObjects());
+               
+		// Now Loop over all objects in the metadata file and fill the logical tree of the experiment
+		long int last_oripart_idx = -1;
+		int nr_frames = 0;
+		FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDimg)
 		{
-
 			// Add new micrographs or get mic_id for existing micrograph
-			FileName mic_name="", group_name="";
-			if (MDimgin.containsLabel(EMDL_MICROGRAPH_NAME))
+  	                mic_id = -1;
+                        long int idx = micrographs.size();
+			long int avg_mic_idx = average_micrographs.size() - 1;
+                        std::string last_mic_name = (idx > 0) ? micrographs[idx-1].name : "";
+			FileName mic_name=""; // Filename instead of string because will decompose below
+			std::string mic_name_after_at="", group_name="", last_mic_name_after_at="", last_ori_mic_name="";
+			if (is_mic_a_movie)
+				last_mic_name_after_at = (idx > 0) ? last_mic_name.substr(last_mic_name.find("@")+1) : "";			
+
+			if (star_contains_micname)
 			{
-				MDimgin.getValue(EMDL_MICROGRAPH_NAME, mic_name);
+				MDimg.getValue(EMDL_MICROGRAPH_NAME, mic_name);
 
-				// If this micrograph did not exist yet, add it to the experiment
-				mic_id = -1;
-				for (long int i = micrographs.size() - 1; i >= 0; i--) // search backwards to find match faster
+				// Now the micrograph names are sorted, but for movies only the part after "@" is sorted....
+				long idx = micrographs.size();
+				if (is_mic_a_movie)
 				{
-					if (micrographs[i].name == mic_name)
+					// Go backward in old micrographs and see if this frame from the movie has been seen already
+					// But only for the same mic_name_after_at!
+					mic_name_after_at = mic_name.substr(mic_name.find("@")+1);
+					if (last_mic_name_after_at == mic_name_after_at)
 					{
-						mic_id = micrographs[i].id;
-						break;
+						// TODO: only go back maximum of nr_frames steps!
+						for (long i = micrographs.size() - 1; i >= 0; i--)
+						{
+							// if we find this frame from this mic_name, then set mic_id and break
+							if (micrographs[i].name == mic_name)
+							{
+								mic_id = micrographs[i].id;
+								break;
+							}
+						}
+					}
+					else
+					{
+						// A new (original) micrograph
+						last_oripart_idx = ori_particles.size();
+						avg_mic_idx = -1;
+						// How many movie frames per micrograph are there? (Only count for first average_micrograph, all the rest is the same)
+						if (average_micrographs.size() == 1)
+						{
+							nr_frames = micrographs.size();
+							ori_particles.reserve(MDimg.numberOfObjects()/nr_frames);
+#ifdef DEBUG_READ							
+							std::cerr << "nr_frames = " << nr_frames << std::endl;
+#endif
+						}
+					}
+				}
+				else // not a movie
+				{
+					if (last_mic_name == mic_name)
+					{
+						// This particle belongs to the previous micrograph
+						mic_id = micrographs[idx - 1].id;
+					}
+					else
+					{
+						// A new micrograph
+						last_oripart_idx = ori_particles.size();
 					}
 				}
+
+				// Make a new micrograph
 				if (mic_id < 0)
 					mic_id = addMicrograph(mic_name);
 
-				// Check whether there is a group label, if not use a group for each micrograph
-				if (MDimgin.containsLabel(EMDL_MLMODEL_GROUP_NAME))
+				// Make a new average_micrograph
+				if (is_mic_a_movie && avg_mic_idx < 0)
 				{
-					MDimgin.getValue(EMDL_MLMODEL_GROUP_NAME, group_name);
+					avg_mic_idx = addAverageMicrograph(mic_name_after_at);
 				}
-				else
-				{
-					group_name = mic_name;
-				}
-				// If this group did not exist yet, add it to the experiment
-				group_id = -1;
-				for (long int i = groups.size() - 1; i >= 0; i--) // search backwards to find match faster
+
+#ifdef DEBUG_READ
+				timer.tic(tgroup);
+#endif
+
+				// For example in particle_polishing the groups are not needed...
+				if (!do_ignore_group_name)
 				{
-					if (groups[i].name == group_name)
+					// Check whether there is a group label, if not use a group for each micrograph
+					if (MDimg.containsLabel(EMDL_MLMODEL_GROUP_NAME))
+						MDimg.getValue(EMDL_MLMODEL_GROUP_NAME, group_name);
+					else
+						group_name = mic_name;
+
+					// If this group did not exist yet, add it to the experiment
+					group_id = -1;
+					for (long int i = groups.size() - 1; i >= 0; i--) // search backwards to find match faster
 					{
-						group_id = groups[i].id;
-						break;
+						if (groups[i].name == group_name)
+						{
+							group_id = groups[i].id;
+							break;
+						}
 					}
+					if (group_id < 0)
+						group_id = addGroup(group_name);
 				}
-				if (group_id < 0)
-					group_id = addGroup(group_name);
+
+#ifdef DEBUG_READ
+				timer.toc(tgroup);
+#endif
+
 			}
 			else
 			{
@@ -889,63 +864,66 @@ void Experiment::read(FileName fn_exp, bool do_ignore_particle_name)
 				group_id = 0;
 			}
 
-			// If there is an EMDL_PARTICLE_RANDOM_SUBSET entry in the input STAR-file, then set the random_subset, otherwise use defualt (0)
+			// If there is an EMDL_PARTICLE_RANDOM_SUBSET entry in the input STAR-file, then set the random_subset, otherwise use default (0)
 			int my_random_subset;
-			if (!MDimgin.getValue(EMDL_PARTICLE_RANDOM_SUBSET, my_random_subset))
+			if (!MDimg.getValue(EMDL_PARTICLE_RANDOM_SUBSET, my_random_subset))
 				my_random_subset = 0;
 
-			// Add this image to an existing particle, or create a new particle
-			std::string part_name;
-			if (MDimgin.containsLabel(EMDL_PARTICLE_NAME) && !do_ignore_particle_name)
-			{
-				MDimgin.getValue(EMDL_PARTICLE_NAME, part_name);
-				// Check whether this particle already exists
-				part_id = -1;
-				for (long int i = particles.size() - 1; i >= 0; i--) // search backwards to find match faster
-				{
-					if (particles[i].name == part_name)
-					{
-						part_id = particles[i].id;
-						break;
-					}
-				}
-				// If no particle with this name was found, then add new one
-				if (part_id < 0)
-				{
-					part_id = addParticle(part_name, my_random_subset);
-				}
-			}
-			else
-			{
-				// If particleName is not in the input metadata file, call this particle by the image number
-				part_name= integerToString(particles.size());
-				part_id = addParticle(part_name, my_random_subset);
-			}
+			// Create a new particle
+			part_id = addParticle(group_id, mic_id, my_random_subset);
+
+#ifdef DEBUG_READ
+			timer.tic(tori);
+#endif
+
+                        if (do_preread_images)
+                        {
+                            FileName fn_img;
+                            MDimg.getValue(EMDL_IMAGE_NAME, fn_img);
+                            Image<DOUBLE> img;
+                            img.read(fn_img);
+                            img().setXmippOrigin();
+                            particles[part_id].img = img();
+                        }
 
 			// Add this particle to an existing OriginalParticle, or create a new OriginalParticle
+			std::string ori_part_name;
 			long int ori_part_id = -1;
-			if (MDimgin.containsLabel(EMDL_PARTICLE_ORI_NAME))
+
+			if (MDimg.containsLabel(EMDL_PARTICLE_ORI_NAME))
+				MDimg.getValue(EMDL_PARTICLE_ORI_NAME, ori_part_name);
+			else
+				MDimg.getValue(EMDL_IMAGE_NAME, ori_part_name);
+
+			if (MDimg.containsLabel(EMDL_PARTICLE_ORI_NAME) && !do_ignore_original_particle_name)
 			{
-				MDimgin.getValue(EMDL_PARTICLE_ORI_NAME, part_name);
-				for (long int i = ori_particles.size() - 1; i >= 0; i--)  // search backwards to find match faster
+				// Only search ori_particles for the last (original) micrograph
+				for (long int i = last_oripart_idx; i < ori_particles.size(); i++)
 				{
-					if (ori_particles[i].name == part_name)
+					if (ori_particles[i].name == ori_part_name)
 					{
 						ori_part_id = i;
 						break;
 					}
 				}
-
-				// If no OriginalParticles with this name was found, then add new one
-				if (ori_part_id < 0)
-					ori_part_id = addOriginalParticle(part_name, my_random_subset);
-
 			}
-			else
+
+			// If no OriginalParticles with this name was found,
+			// or if no EMDL_PARTICLE_ORI_NAME in the input file, or if do_ignore_original_particle_name
+			// then add a new ori_particle
+			if (ori_part_id < 0)
 			{
-				// If there are no EMDL_PARTICLE_ORI_NAME in the input file: just one particle per OriginalParticle
-				ori_part_id = addOriginalParticle(part_name, my_random_subset);
+				ori_part_id = addOriginalParticle(ori_part_name, my_random_subset);
+				// Also add this original_particle to an original_micrograph (only for movies)
+				if (is_mic_a_movie)
+				{	
+					average_micrographs[avg_mic_idx].ori_particles_id.push_back(ori_part_id);
+				}
 			}
+#ifdef DEBUG_READ
+			timer.toc(tori);
+#endif
+
 
 			// Add this particle to the OriginalParticle
 			std::string fnt;
@@ -953,87 +931,42 @@ void Experiment::read(FileName fn_exp, bool do_ignore_particle_name)
 			mic_name.decompose(my_order, fnt);
 			(ori_particles[ori_part_id]).addParticle(part_id, my_random_subset, my_order);
 
-			long int img_id = addImage(group_id, mic_id, part_id);
-			// Copy the current row of MDimgin into the current row of MDimg
-			MDimg.setObject(MDimgin.getObject(), img_id);
-
 			// The group number is only set upon reading: it is not read from the STAR file itself,
 			// there the only thing that matters is the order of the micrograph_names
 			// Write igroup+1, to start numbering at one instead of at zero
-			MDimg.setValue(EMDL_MLMODEL_GROUP_NO, group_id + 1, img_id);
+			MDimg.setValue(EMDL_MLMODEL_GROUP_NO, group_id + 1, part_id);
 
 #ifdef DEBUG_READ
 			nr_read++;
 #endif
-		} // end loop over all objects in MDimgin
+		} // end loop over all objects in MDimg
 
 #ifdef DEBUG_READ
+		timer.toc(tfill);
+		timer.tic(tdef);
 		std::cerr << " MDimg.lastObject()= " << MDimg.lastObject() << std::endl;
-		std::cerr << " nr_read= " << nr_read << " particles.size()= " << particles.size() << " micrographs.size()= " << micrographs.size();
+		std::cerr << " nr_read= " << nr_read << " particles.size()= " << particles.size() << " ori_particles.size()= " << ori_particles.size()  << " micrographs.size()= " << micrographs.size() << " average_micrographs.size()= " << average_micrographs.size() << " groups.size()= " << groups.size() << std::endl;
 #endif
 
-		// Now, if this file was created by this class then also fill MDmic:
-		if (contains_images_block)
-		{
-			MDmicin.read(fn_exp, "micrographs");
-			if (!MDmicin.isEmpty())
-			{
-				std::vector<std::string> found_mic_names;
-				FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDmicin)
-				{
-					std::string mic_name;
-					if (!MDmicin.getValue(EMDL_MICROGRAPH_NAME, mic_name))
-						REPORT_ERROR("Experiment::read ERROR: data_micrographs block should contain micrographName labels!");
-					found_mic_names.push_back(mic_name);
-					bool found = false;
-					for (long int i = 0; i < micrographs.size(); i++)
-					{
-						if (micrographs[i].name == mic_name)
-						{
-							// Copy entire row from MDmicin to MDmic (at line micrographs[i].id)
-							MDmic.setObject(MDmicin.getObject(), micrographs[i].id);
-							break;
-						}
-					}
-				}
-
-				// Also check whether all micrographs have been found....
-				for (int i = 0; i < micrographs.size(); i++)
-				{
-					bool found = false;
-					for (int j = 0; j < found_mic_names.size(); j++)
-					{
-						if (found_mic_names[j] == micrographs[i].name)
-						{
-							found = true;
-							break;
-						}
-					}
-					if (!found)
-						REPORT_ERROR("Did not find the following micrograph in the data_micrographs table: " + micrographs[i].name);
-				}
-
-			}
-		}
 	}
 
 #ifdef DEBUG_READ
 	std::cerr << "Done filling MDimg" << std::endl;
-	std::cerr << "Press any key to continue..." << std::endl;
-	std::cin >> c;
+	//std::cerr << "Press any key to continue..." << std::endl;
+	//std::cin >> c;
 #endif
 
 	// Make sure some things are always set in the MDimg
-	bool have_rot  = MDimgin.containsLabel(EMDL_ORIENT_ROT);
-	bool have_tilt = MDimgin.containsLabel(EMDL_ORIENT_TILT);
-	bool have_psi  = MDimgin.containsLabel(EMDL_ORIENT_PSI);
-	bool have_xoff = MDimgin.containsLabel(EMDL_ORIENT_ORIGIN_X);
-	bool have_yoff = MDimgin.containsLabel(EMDL_ORIENT_ORIGIN_Y);
-	bool have_clas = MDimgin.containsLabel(EMDL_PARTICLE_CLASS);
-	bool have_norm = MDimgin.containsLabel(EMDL_IMAGE_NORM_CORRECTION);
+	bool have_rot  = MDimg.containsLabel(EMDL_ORIENT_ROT);
+	bool have_tilt = MDimg.containsLabel(EMDL_ORIENT_TILT);
+	bool have_psi  = MDimg.containsLabel(EMDL_ORIENT_PSI);
+	bool have_xoff = MDimg.containsLabel(EMDL_ORIENT_ORIGIN_X);
+	bool have_yoff = MDimg.containsLabel(EMDL_ORIENT_ORIGIN_Y);
+	bool have_clas = MDimg.containsLabel(EMDL_PARTICLE_CLASS);
+	bool have_norm = MDimg.containsLabel(EMDL_IMAGE_NORM_CORRECTION);
 	FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDimg)
 	{
-		double dzero=0., done=1.;
+		DOUBLE dzero=0., done=1.;
 		int izero = 0;
 		if (!have_rot)
 			MDimg.setValue(EMDL_ORIENT_ROT, dzero);
@@ -1052,14 +985,16 @@ void Experiment::read(FileName fn_exp, bool do_ignore_particle_name)
 	}
 
 #ifdef DEBUG_READ
+	timer.toc(tdef);
 	std::cerr << "Done setting defaults MDimg" << std::endl;
-	std::cerr << "Press any key to continue..." << std::endl;
-	std::cin >> c;
+	timer.tic(tend);
+	//std::cerr << "Press any key to continue..." << std::endl;
+	//std::cin >> c;
 #endif
-
+	
 	// Also set the image_size (use the last image for that, still in fn_img)
 	FileName fn_img;
-	Image<double> img;
+	Image<DOUBLE> img;
 	MDimg.getValue(EMDL_IMAGE_NAME, fn_img, MDimg.firstObject());
 	if (fn_img != "")
 	{
@@ -1090,9 +1025,12 @@ void Experiment::read(FileName fn_exp, bool do_ignore_particle_name)
 	orderParticlesInOriginalParticles();
 
 #ifdef DEBUG_READ
-	std::cerr << "Writing out debug_data.star" << std::endl;
-	write("debug");
-	exit(0);
+	timer.toc(tend);
+	timer.toc(tall);
+	timer.printTimes(false);
+	//std::cerr << "Writing out debug_data.star" << std::endl;
+	//write("debug");
+	//exit(0);
 #endif
 }
 
@@ -1109,14 +1047,6 @@ void Experiment::write(FileName fn_root)
     // Always write MDimg
     MDimg.write(fh);
 
-    // Only write MDmic if more than one micrograph is stored there...
-    //if (MDmic.lastObject() > 1)
-    //	MDmic.write(fh);
-
-    // Only write MDexp if something is stored there...
-    //if (!MDexp.isEmpty())
-    //	MDexp.write(fh);
-
 	fh.close();
 
 }
diff --git a/src/exp_model.h b/src/exp_model.h
index 482b616..d531d77 100644
--- a/src/exp_model.h
+++ b/src/exp_model.h
@@ -24,55 +24,36 @@
 #include "src/image.h"
 #include "src/multidim_array.h"
 #include "src/metadata_table.h"
-
+#include "src/time.h"
+
+/// Reserve large vectors with some reasonable estimate
+// Larger numbers will still be OK, but memory management might suffer 
+#define MAX_NR_PARTICLES_PER_MICROGRAPH 1000
+#define MAX_NR_MICROGRAPHS 2000
+#define MAX_NR_FRAMES_PER_MOVIE 100
+ 
 ////////////// Hierarchical metadata model for tilt series
 
-// ExpImage, ExpParticle, and ExpMicrograph dont store any metadata other than logical relationships
-// All image and micrograph -related metadata is stored in MDimg and MDmic inside Experiment...
-class ExpImage
+class ExpParticle
 {
 public:
-	// ID of this image, i.e. which number in the MDimg am I?
+	// Particle id
 	long int id;
 
-	// ID of the micrograph that this image comes from
+	// ID of the micrograph that this particle comes from
 	long int micrograph_id;
 
-	// ID of the group that this image comes from
+	// ID of the group that this particle comes from
 	long int group_id;
 
-	// ID of the particle that this image comes from
-	long int particle_id;
-
-	// Empty constructor
-	ExpImage() {};
-
-	// Destructor needed for work with vectors
-	~ExpImage() {};
-
-	void clear()
-	{
-		id = micrograph_id = group_id = particle_id = -1;
-	}
-};
-
-class ExpParticle
-{
-public:
-	// Particle id
-	long int id;
-
-	// Name of this particle (by this name it will be recognised upon reading)
-	std::string name;
-
 	// Random subset this particle belongs to
 	int random_subset;
 
-	// All the images that were recorded for this particle
-	std::vector<ExpImage> images;
+        // Pre-read array of the image in RAM
+        MultidimArray<DOUBLE> img;
 
 	// Empty Constructor
-	ExpParticle(int max_nr_images_pers_particle = 1)
+	ExpParticle()
 	{
 		clear();
 	}
@@ -86,10 +67,9 @@ public:
 	// Initialise
 	void clear()
 	{
-		id = -1;
+		id = micrograph_id = group_id = -1;
 		random_subset = 0;
-		name="undefined";
-		images.clear();
+                img.clear();
 	}
 
 };
@@ -127,6 +107,8 @@ public:
 		name="undefined";
 		particles_id.clear();
 		particles_order.clear();
+		particles_id.reserve(MAX_NR_FRAMES_PER_MOVIE);
+		particles_order.reserve(MAX_NR_FRAMES_PER_MOVIE); 
 	}
 
 	void addParticle(long int _particle_id, int _random_subset, int _order);
@@ -158,7 +140,7 @@ public:
 		clear();
 	}
 
-	// Copy constructor needed for work with vectors
+
 	AverageMicrograph(AverageMicrograph const& copy)
 	{
 		id = copy.id;
@@ -166,7 +148,6 @@ public:
 		ori_particles_id = copy.ori_particles_id;
 	}
 
-	// Define assignment operator in terms of the copy constructor
 	AverageMicrograph& operator=(AverageMicrograph const& copy)
 	{
 		id = copy.id;
@@ -175,12 +156,14 @@ public:
 		return *this;
 	}
 
+
 	// Initialise
 	void clear()
 	{
 		id = -1;
 		name="";
 		ori_particles_id.clear();
+		ori_particles_id.reserve(MAX_NR_PARTICLES_PER_MICROGRAPH);
 	}
 };
 
@@ -194,8 +177,8 @@ public:
 	// Name of this micrograph (by this name it will be recognised upon reading)
 	std::string name;
 
-	// All the images that were recorded on this micrograph
-	std::vector<ExpImage> images;
+	// All the particles that were recorded on this micrograph
+	std::vector<long int> particle_ids;
 
 	// Empty Constructor
 	ExpMicrograph()
@@ -214,7 +197,7 @@ public:
 	{
 		id = copy.id;
 		name = copy.name;
-		images = copy.images;
+		particle_ids = copy.particle_ids;
 
 	}
 
@@ -223,7 +206,7 @@ public:
 	{
 		id = copy.id;
 		name = copy.name;
-		images = copy.images;
+		particle_ids = copy.particle_ids;
 		return *this;
 	}
 
@@ -232,7 +215,8 @@ public:
 	{
 		id = -1;
 		name="";
-		images.clear();
+		particle_ids.clear();
+		particle_ids.reserve(MAX_NR_PARTICLES_PER_MICROGRAPH);
 	}
 
 };
@@ -328,8 +312,10 @@ public:
 	{
 		groups.clear();
 		micrographs.clear();
-		particles.clear();
-		ori_particles.clear();
+		micrographs.reserve(MAX_NR_MICROGRAPHS);
+		groups.reserve(MAX_NR_MICROGRAPHS);
+		particles.clear(); // reserve upon reading
+		ori_particles.clear(); // TODO: reserve upon reading
 		MDexp.clear();
 		MDexp.setIsList(true);
 		MDimg.clear();
@@ -341,9 +327,6 @@ public:
 		MDexp.setName("experiment");
 	}
 
-	// Calculate the total number of images in this experiment
-	long int numberOfImages(int random_subset = 0);
-
 	// Calculate the total number of particles in this experiment
 	long int numberOfParticles(int random_subset = 0);
 
@@ -356,38 +339,20 @@ public:
 	// Calculate the total number of groups in this experiment
 	long int numberOfGroups();
 
-	// Get the number of images for this particle
-	int getNrImagesInSeries(long int part_id);
-
 	// Get the random_subset for this particle
 	int getRandomSubset(long int part_id);
 
 	// Get the micrograph_id for the N'th image for this particle
-	long int getMicrographId(long int part_id, int inseries_no);
+	long int getMicrographId(long int part_id);
 
 	// Get the group_id for the N'th image for this particle
-	long int getGroupId(long int part_id, int inseries_no);
-
-	// Get the image_id for the N'th image for this particle
-	long int getImageId(long int part_id, int inseries_no);
+	long int getGroupId(long int part_id);
 
 	// Get the metadata-row for this image in a separate MetaDataTable
-	MetaDataTable getMetaDataImage(long int part_id, int inseries_no);
-
-	// Get the metadata-row for this micrograph in a separate MetaDataTable
-	MetaDataTable getMetaDataMicrograph(long int part_id, int inseries_no);
-
-	// Get the micrograph transformation matrix for this particle
-	Matrix2D<double> getMicrographTransformationMatrix(long int mic_id);
-
-	// Get the micrograph transformation matrix for this particle & iseries_no
-	Matrix2D<double> getMicrographTransformationMatrix(long int part_id, int inseries_no);
-
-	// Add an image
-	long int addImage(long int group_id, long int micrograph_id, long int particle_id);
+	MetaDataTable getMetaDataImage(long int part_id);
 
 	// Add a particle
-	long int addParticle(std::string part_name, int random_subset = 0);
+	long int addParticle(long int group_id, long int micrograph_id, int random_subset = 0);
 
 	// Add an original particle
 	long int addOriginalParticle(std::string part_name, int random_subset = 0);
@@ -414,21 +379,17 @@ public:
 	// rlnParticleName entries in the movie-frame Experiment should coincide with rlnImageName entries in the current Experiment
 	// the entries rlnAngleRot, rlnAngleTilt, rlnAnglePsi, rlnOriginX and rlnOriginY will be taken from the current Experiment and
 	// copied into the new moevieframe Experiment. In addition, these values will be used to center the corresponding Priors
-	void expandToMovieFrames(FileName fn_data_movie);
+	void expandToMovieFrames(FileName fn_data_movie, int verb = 0);
 
 	// Make sure the particles inside each orriginal_particle are in the right order
 	// After they have been ordered, get rid of the particles_order vector inside the ori_particles
 	void orderParticlesInOriginalParticles();
 
-	// For all OriginalParticles, determine from which AverageMicrograph they originate
-	// This is only used for movie-processing, e.g. in the particle_polisher
-	void getAverageMicrographs();
-
 	// Print help message for possible command-line options
 	void usage();
 
 	// Read from file
-	void read(FileName fn_in, bool do_ignore_particle_name = false);
+	void read(FileName fn_in, bool do_ignore_original_particle_name = false, bool do_ignore_group_name = false, bool do_preread_images = false);
 
 	// Write
 	void write(FileName fn_root);
diff --git a/src/fftw.cpp b/src/fftw.cpp
index 61f3e20..3d19a78 100644
--- a/src/fftw.cpp
+++ b/src/fftw.cpp
@@ -48,7 +48,11 @@
 #include <string.h>
 #include <pthread.h>
 
+#ifdef FLOAT_PRECISION
+static pthread_mutex_t fftwf_plan_mutex = PTHREAD_MUTEX_INITIALIZER;
+#else
 static pthread_mutex_t fftw_plan_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
 
 //#define DEBUG_PLANS
 
@@ -106,10 +110,18 @@ void FourierTransformer::cleanup()
     clear();
     // Then clean up all the junk fftw keeps lying around
     // SOMEHOW THE FOLLOWING IS NOT ALLOWED WHEN USING MULTPLE TRANSFORMER OBJECTS....
-	if (threadsSetOn)
+#ifdef FLOAT_PRECISION
+    if (threadsSetOn)
+    	fftwf_cleanup_threads();
+    else
+    	fftwf_cleanup();
+#else
+    if (threadsSetOn)
     	fftw_cleanup_threads();
     else
     	fftw_cleanup();
+#endif
+
 #ifdef DEBUG_PLANS
     std::cerr << "CLEANED-UP this= "<<this<< std::endl;
 #endif
@@ -120,22 +132,21 @@ void FourierTransformer::destroyPlans()
 {
 
     // Anything to do with plans has to be protected for threads!
+#ifdef FLOAT_PRECISION
+    pthread_mutex_lock(&fftwf_plan_mutex);
+    if (fPlanForward !=NULL)
+   	fftwf_destroy_plan(fPlanForward);
+    if (fPlanBackward!=NULL)
+   	fftwf_destroy_plan(fPlanBackward);
+   pthread_mutex_unlock(&fftwf_plan_mutex);
+#else
     pthread_mutex_lock(&fftw_plan_mutex);
     if (fPlanForward !=NULL)
-    {
-#ifdef DEBUG_PLANS
-    	std::cerr << " DESTROY fPlanForward= " << fPlanForward  <<" this= "<<this<<std::endl;
-#endif
-    	fftw_destroy_plan(fPlanForward);
-    }
+   	fftw_destroy_plan(fPlanForward);
     if (fPlanBackward!=NULL)
-    {
-#ifdef DEBUG_PLANS
-    	std::cerr << " DESTROY fPlanBackward= " << fPlanBackward  <<" this= "<<this<< std::endl;
-#endif
    	fftw_destroy_plan(fPlanBackward);
-    }
-    pthread_mutex_unlock(&fftw_plan_mutex);
+   pthread_mutex_unlock(&fftw_plan_mutex);
+#endif
 
 }
 
@@ -145,16 +156,24 @@ void FourierTransformer::setThreadsNumber(int tNumber)
     {
         threadsSetOn=true;
         nthreads = tNumber;
+#ifdef FLOAT_PRECISION
+        pthread_mutex_lock(&fftwf_plan_mutex);
+        if(fftwf_init_threads()==0)
+            REPORT_ERROR("FFTW cannot init threads (setThreadsNumber)");
+        fftwf_plan_with_nthreads(nthreads);
+        pthread_mutex_unlock(&fftwf_plan_mutex);
+#else
         pthread_mutex_lock(&fftw_plan_mutex);
         if(fftw_init_threads()==0)
             REPORT_ERROR("FFTW cannot init threads (setThreadsNumber)");
         fftw_plan_with_nthreads(nthreads);
         pthread_mutex_unlock(&fftw_plan_mutex);
+#endif
     }
 }
 
 // Initialization ----------------------------------------------------------
-const MultidimArray<double> &FourierTransformer::getReal() const
+const MultidimArray<DOUBLE> &FourierTransformer::getReal() const
 {
     return (*fReal);
 }
@@ -165,7 +184,7 @@ const MultidimArray<Complex > &FourierTransformer::getComplex() const
 }
 
 
-void FourierTransformer::setReal(MultidimArray<double> &input)
+void FourierTransformer::setReal(MultidimArray<DOUBLE> &input)
 {
     bool recomputePlan=false;
     if (fReal==NULL)
@@ -206,6 +225,15 @@ void FourierTransformer::setReal(MultidimArray<double> &input)
         // Destroy both forward and backward plans if they already exist
         destroyPlans();
         // Make new plans
+#ifdef FLOAT_PRECISION
+        pthread_mutex_lock(&fftwf_plan_mutex);
+        fPlanForward = fftwf_plan_dft_r2c(ndim, N, MULTIDIM_ARRAY(*fReal),
+                                         (fftwf_complex*) MULTIDIM_ARRAY(fFourier), FFTW_ESTIMATE);
+        fPlanBackward = fftwf_plan_dft_c2r(ndim, N,
+                                          (fftwf_complex*) MULTIDIM_ARRAY(fFourier), MULTIDIM_ARRAY(*fReal),
+                                          FFTW_ESTIMATE);
+        pthread_mutex_unlock(&fftwf_plan_mutex);
+#else
         pthread_mutex_lock(&fftw_plan_mutex);
         fPlanForward = fftw_plan_dft_r2c(ndim, N, MULTIDIM_ARRAY(*fReal),
                                          (fftw_complex*) MULTIDIM_ARRAY(fFourier), FFTW_ESTIMATE);
@@ -213,6 +241,7 @@ void FourierTransformer::setReal(MultidimArray<double> &input)
                                           (fftw_complex*) MULTIDIM_ARRAY(fFourier), MULTIDIM_ARRAY(*fReal),
                                           FFTW_ESTIMATE);
         pthread_mutex_unlock(&fftw_plan_mutex);
+#endif
 
         if (fPlanForward == NULL || fPlanBackward == NULL)
             REPORT_ERROR("FFTW plans cannot be created");
@@ -264,6 +293,24 @@ void FourierTransformer::setReal(MultidimArray<Complex > &input)
             break;
         }
 
+#ifdef FLOAT_PRECISION
+        pthread_mutex_lock(&fftwf_plan_mutex);
+        if (fPlanForward!=NULL)
+            fftwf_destroy_plan(fPlanForward);
+        fPlanForward=NULL;
+        fPlanForward = fftwf_plan_dft(ndim, N, (fftwf_complex*) MULTIDIM_ARRAY(*fComplex),
+                                     (fftwf_complex*) MULTIDIM_ARRAY(fFourier), FFTW_FORWARD, FFTW_ESTIMATE);
+        if (fPlanBackward!=NULL)
+            fftwf_destroy_plan(fPlanBackward);
+        fPlanBackward=NULL;
+        fPlanBackward = fftwf_plan_dft(ndim, N, (fftwf_complex*) MULTIDIM_ARRAY(fFourier),
+                                      (fftwf_complex*) MULTIDIM_ARRAY(*fComplex), FFTW_BACKWARD, FFTW_ESTIMATE);
+        if (fPlanForward == NULL || fPlanBackward == NULL)
+            REPORT_ERROR("FFTW plans cannot be created");
+        delete [] N;
+        complexDataPtr=MULTIDIM_ARRAY(*fComplex);
+        pthread_mutex_unlock(&fftwf_plan_mutex);
+#else
         pthread_mutex_lock(&fftw_plan_mutex);
         if (fPlanForward!=NULL)
             fftw_destroy_plan(fPlanForward);
@@ -280,13 +327,14 @@ void FourierTransformer::setReal(MultidimArray<Complex > &input)
         delete [] N;
         complexDataPtr=MULTIDIM_ARRAY(*fComplex);
         pthread_mutex_unlock(&fftw_plan_mutex);
+#endif
     }
 }
 
 void FourierTransformer::setFourier(MultidimArray<Complex > &inputFourier)
 {
     memcpy(MULTIDIM_ARRAY(fFourier),MULTIDIM_ARRAY(inputFourier),
-           MULTIDIM_SIZE(inputFourier)*2*sizeof(double));
+           MULTIDIM_SIZE(inputFourier)*2*sizeof(DOUBLE));
 }
 
 // Transform ---------------------------------------------------------------
@@ -294,8 +342,11 @@ void FourierTransformer::Transform(int sign)
 {
     if (sign == FFTW_FORWARD)
     {
+#ifdef FLOAT_PRECISION
+        fftwf_execute(fPlanForward);
+#else
         fftw_execute(fPlanForward);
-
+#endif
         // Normalisation of the transform
         unsigned long int size=0;
         if(fReal!=NULL)
@@ -309,8 +360,13 @@ void FourierTransformer::Transform(int sign)
             DIRECT_MULTIDIM_ELEM(fFourier,n) /= size;
     }
     else if (sign == FFTW_BACKWARD)
+    {
+#ifdef FLOAT_PRECISION
+        fftwf_execute(fPlanBackward);
+#else
         fftw_execute(fPlanBackward);
-
+#endif
+    }
 }
 
 void FourierTransformer::FourierTransform()
@@ -380,7 +436,7 @@ void FourierTransformer::enforceHermitianSymmetry()
 }
 
 
-void randomizePhasesBeyond(MultidimArray<double> &v, int index)
+void randomizePhasesBeyond(MultidimArray<DOUBLE> &v, int index)
 {
     MultidimArray< Complex > FT;
     FourierTransformer transformer;
@@ -392,10 +448,10 @@ void randomizePhasesBeyond(MultidimArray<double> &v, int index)
     {
     	if (kp*kp + ip*ip + jp*jp >= index2)
     	{
-    		double mag = abs(DIRECT_A3D_ELEM(FT, k, i, j));
-    		double phas = rnd_unif(0., 2.*PI);
-    		double realval = mag * cos(phas);
-    		double imagval = mag * sin(phas);
+    		DOUBLE mag = abs(DIRECT_A3D_ELEM(FT, k, i, j));
+    		DOUBLE phas = rnd_unif(0., 2.*PI);
+    		DOUBLE realval = mag * cos(phas);
+    		DOUBLE imagval = mag * sin(phas);
     		DIRECT_A3D_ELEM(FT, k, i, j) = Complex(realval, imagval);
     	}
     }
@@ -410,14 +466,14 @@ void randomizePhasesBeyond(MultidimArray<double> &v, int index)
 // from precalculated Fourier Transforms, and without sampling rate etc.
 void getFSC(MultidimArray< Complex > &FT1,
 			MultidimArray< Complex > &FT2,
-			MultidimArray< double > &fsc)
+			MultidimArray< DOUBLE > &fsc)
 {
 	if (!FT1.sameShape(FT2))
         REPORT_ERROR("fourierShellCorrelation ERROR: MultidimArrays have different shapes!");
 
     MultidimArray< int > radial_count(XSIZE(FT1));
-    MultidimArray<double> num, den1, den2;
-    Matrix1D<double> f(3);
+    MultidimArray<DOUBLE> num, den1, den2;
+    Matrix1D<DOUBLE> f(3);
     num.initZeros(radial_count);
     den1.initZeros(radial_count);
     den2.initZeros(radial_count);
@@ -429,8 +485,8 @@ void getFSC(MultidimArray< Complex > &FT1,
         	continue;
         Complex z1=DIRECT_A3D_ELEM(FT1, k, i, j);
         Complex z2=DIRECT_A3D_ELEM(FT2, k, i, j);
-        double absz1=abs(z1);
-        double absz2=abs(z2);
+        DOUBLE absz1=abs(z1);
+        DOUBLE absz2=abs(z2);
         num(idx)+= (conj(z1) * z2).real;
         den1(idx)+= absz1*absz1;
         den2(idx)+= absz2*absz2;
@@ -445,9 +501,9 @@ void getFSC(MultidimArray< Complex > &FT1,
 }
 
 
-void getFSC(MultidimArray< double > &m1,
-		    MultidimArray< double > &m2,
-		    MultidimArray< double > &fsc)
+void getFSC(MultidimArray< DOUBLE > &m1,
+		    MultidimArray< DOUBLE > &m2,
+		    MultidimArray< DOUBLE > &fsc)
 {
 	MultidimArray< Complex > FT1, FT2;
 	FourierTransformer transformer;
@@ -457,7 +513,7 @@ void getFSC(MultidimArray< double > &m1,
 }
 
 /*
-void selfScaleToSizeFourier(long int Ydim, long int Xdim, MultidimArray<double>& Mpmem, int nThreads)
+void selfScaleToSizeFourier(long int Ydim, long int Xdim, MultidimArray<DOUBLE>& Mpmem, int nThreads)
 {
 
     //Mmem = *this
@@ -493,19 +549,94 @@ void selfScaleToSizeFourier(long int Ydim, long int Xdim, MultidimArray<double>&
 }
 */
 
+
+void getAbMatricesForShiftImageInFourierTransform(MultidimArray<Complex > &in,
+        MultidimArray<Complex > &out,
+		  TabSine &tab_sin, TabCosine &tab_cos,
+		  DOUBLE oridim, DOUBLE xshift, DOUBLE yshift, DOUBLE zshift)
+{
+	out.resize(in);
+	DOUBLE dotp, a, b, x, y, z;
+	switch (in.getDim())
+	{
+	case 1:
+		xshift /= -oridim;
+		for (long int j = 0; j < XSIZE(in); j++)
+		{
+			x = j;
+			dotp = 2 * PI * (x * xshift);
+			a = tab_cos(dotp);
+			b = tab_sin(dotp);
+			DIRECT_A1D_ELEM(out, j) = Complex(a, b);
+		}
+		break;
+	case 2:
+		xshift /= -oridim;
+		yshift /= -oridim;
+		for (long int i=0; i<XSIZE(in); i++)
+			for (long int j=0; j<XSIZE(in); j++)
+			{
+				x = j;
+				y = i;
+				dotp = 2 * PI * (x * xshift + y * yshift);
+				a = tab_cos(dotp);
+				b = tab_sin(dotp);
+				DIRECT_A2D_ELEM(out, i, j) = Complex(a, b);
+			}
+		for (long int i=YSIZE(in)-1; i>=XSIZE(in); i--)
+		{
+			y = i - YSIZE(in);
+			for (long int j=0; j<XSIZE(in); j++)
+			{
+				x = j;
+				dotp = 2 * PI * (x * xshift + y * yshift);
+				a = tab_cos(dotp);
+				b = tab_sin(dotp);
+				DIRECT_A2D_ELEM(out, i, j) = Complex(a, b);
+			}
+		}
+		break;
+	case 3:
+		xshift /= -oridim;
+		yshift /= -oridim;
+		zshift /= -oridim;
+		for (long int k=0; k<ZSIZE(in); k++)
+		{
+			z = (k < XSIZE(in)) ? k : k - ZSIZE(in);
+			for (long int i=0; i<YSIZE(in); i++)
+			{
+				y = (i < XSIZE(in)) ? i : i - YSIZE(in);
+				for (long int j=0; j<XSIZE(in); j++)
+				{
+					x = j;
+					dotp = 2 * PI * (x * xshift + y * yshift + z * zshift);
+					a = tab_cos(dotp);
+					b = tab_sin(dotp);
+					DIRECT_A3D_ELEM(out, k, i, j) = Complex(a, b);
+				}
+			}
+		}
+		break;
+	default:
+		REPORT_ERROR("getAbMatricesForShiftImageInFourierTransform ERROR: dimension should be 1, 2 or 3!");
+	}
+
+
+}
+
+
 // Shift an image through phase-shifts in its Fourier Transform
 void shiftImageInFourierTransform(MultidimArray<Complex > &in,
 		                          MultidimArray<Complex > &out,
 								  TabSine &tab_sin, TabCosine &tab_cos,
-								  double oridim, Matrix1D<double> shift)
+								  DOUBLE oridim, DOUBLE xshift, DOUBLE yshift, DOUBLE zshift)
 {
 	out.resize(in);
-	shift /= -oridim;
-	double dotp, a, b, c, d, ac, bd, ab_cd, x, y, z, xshift, yshift, zshift;
+	DOUBLE dotp, a, b, c, d, ac, bd, ab_cd, x, y, z;
 	switch (in.getDim())
 	{
 	case 1:
-		xshift = XX(shift);
+		xshift /= -oridim;
 		if (ABS(xshift) < XMIPP_EQUAL_ACCURACY)
 		{
 			out = in;
@@ -526,8 +657,8 @@ void shiftImageInFourierTransform(MultidimArray<Complex > &in,
 		}
 		break;
 	case 2:
-		xshift = XX(shift);
-		yshift = YY(shift);
+		xshift /= -oridim;
+		yshift /= -oridim;
 		if (ABS(xshift) < XMIPP_EQUAL_ACCURACY && ABS(yshift) < XMIPP_EQUAL_ACCURACY)
 		{
 			out = in;
@@ -567,9 +698,9 @@ void shiftImageInFourierTransform(MultidimArray<Complex > &in,
 		}
 		break;
 	case 3:
-		xshift = XX(shift);
-		yshift = YY(shift);
-		zshift = ZZ(shift);
+		xshift /= -oridim;
+		yshift /= -oridim;
+		zshift /= -oridim;
 		if (ABS(xshift) < XMIPP_EQUAL_ACCURACY && ABS(yshift) < XMIPP_EQUAL_ACCURACY && ABS(zshift) < XMIPP_EQUAL_ACCURACY)
 		{
 			out = in;
@@ -585,8 +716,8 @@ void shiftImageInFourierTransform(MultidimArray<Complex > &in,
 				{
 					x = j;
 					dotp = 2 * PI * (x * xshift + y * yshift + z * zshift);
-					a = cos(dotp);
-					b = sin(dotp);
+					a = tab_cos(dotp);
+					b = tab_sin(dotp);
 					c = DIRECT_A3D_ELEM(in, k, i, j).real;
 					d = DIRECT_A3D_ELEM(in, k, i, j).imag;
 					ac = a * c;
@@ -604,15 +735,14 @@ void shiftImageInFourierTransform(MultidimArray<Complex > &in,
 // Shift an image through phase-shifts in its Fourier Transform (without pretabulated sine and cosine)
 void shiftImageInFourierTransform(MultidimArray<Complex > &in,
 		                          MultidimArray<Complex > &out,
-		                          double oridim, Matrix1D<double> shift)
+		                          DOUBLE oridim, DOUBLE xshift, DOUBLE yshift, DOUBLE zshift)
 {
 	out.resize(in);
-	shift /= -oridim;
-	double dotp, a, b, c, d, ac, bd, ab_cd, x, y, z, xshift, yshift, zshift;
+	DOUBLE dotp, a, b, c, d, ac, bd, ab_cd, x, y, z;
 	switch (in.getDim())
 	{
 	case 1:
-		xshift = XX(shift);
+		xshift /= -oridim;
 		if (ABS(xshift) < XMIPP_EQUAL_ACCURACY)
 		{
 			out = in;
@@ -633,8 +763,8 @@ void shiftImageInFourierTransform(MultidimArray<Complex > &in,
 		}
 		break;
 	case 2:
-		xshift = XX(shift);
-		yshift = YY(shift);
+		xshift /= -oridim;
+		yshift /= -oridim;
 		if (ABS(xshift) < XMIPP_EQUAL_ACCURACY && ABS(yshift) < XMIPP_EQUAL_ACCURACY)
 		{
 			out = in;
@@ -674,9 +804,9 @@ void shiftImageInFourierTransform(MultidimArray<Complex > &in,
 		}
 		break;
 	case 3:
-		xshift = XX(shift);
-		yshift = YY(shift);
-		zshift = ZZ(shift);
+		xshift /= -oridim;
+		yshift /= -oridim;
+		zshift /= -oridim;
 		if (ABS(xshift) < XMIPP_EQUAL_ACCURACY && ABS(yshift) < XMIPP_EQUAL_ACCURACY && ABS(zshift) < XMIPP_EQUAL_ACCURACY)
 		{
 			out = in;
@@ -709,15 +839,15 @@ void shiftImageInFourierTransform(MultidimArray<Complex > &in,
 	}
 }
 
-void getSpectrum(MultidimArray<double> &Min,
-                 MultidimArray<double> &spectrum,
+void getSpectrum(MultidimArray<DOUBLE> &Min,
+                 MultidimArray<DOUBLE> &spectrum,
                  int spectrum_type)
 {
 
     MultidimArray<Complex > Faux;
     int xsize = XSIZE(Min);
-    Matrix1D<double> f(3);
-    MultidimArray<double> count(xsize);
+    Matrix1D<DOUBLE> f(3);
+    MultidimArray<DOUBLE> count(xsize);
     FourierTransformer transformer;
 
     spectrum.initZeros(xsize);
@@ -739,12 +869,12 @@ void getSpectrum(MultidimArray<double> &Min,
 
 }
 
-void divideBySpectrum(MultidimArray<double> &Min,
-                      MultidimArray<double> &spectrum,
+void divideBySpectrum(MultidimArray<DOUBLE> &Min,
+                      MultidimArray<DOUBLE> &spectrum,
                       bool leave_origin_intact)
 {
 
-    MultidimArray<double> div_spec(spectrum);
+    MultidimArray<DOUBLE> div_spec(spectrum);
     FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY1D(spectrum)
     {
         if (ABS(dAi(spectrum,i)) > 0.)
@@ -755,17 +885,16 @@ void divideBySpectrum(MultidimArray<double> &Min,
     multiplyBySpectrum(Min,div_spec,leave_origin_intact);
 }
 
-void multiplyBySpectrum(MultidimArray<double> &Min,
-                        MultidimArray<double> &spectrum,
+void multiplyBySpectrum(MultidimArray<DOUBLE> &Min,
+                        MultidimArray<DOUBLE> &spectrum,
                         bool leave_origin_intact)
 {
 
     MultidimArray<Complex > Faux;
-    Matrix1D<double> f(3);
-    MultidimArray<double> lspectrum;
+    Matrix1D<DOUBLE> f(3);
+    MultidimArray<DOUBLE> lspectrum;
     FourierTransformer transformer;
-    double dim3 = XSIZE(Min)*YSIZE(Min)*ZSIZE(Min);
-
+    //DOUBLE dim3 = XSIZE(Min)*YSIZE(Min)*ZSIZE(Min);
     transformer.FourierTransform(Min, Faux, false);
     lspectrum=spectrum;
     if (leave_origin_intact)
@@ -773,34 +902,34 @@ void multiplyBySpectrum(MultidimArray<double> &Min,
     FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(Faux)
     {
     	long int idx = ROUND(sqrt(kp*kp + ip*ip + jp*jp));
-        dAkij(Faux, k, i, j) *=  lspectrum(idx) * dim3;
+        dAkij(Faux, k, i, j) *=  lspectrum(idx);// * dim3;
     }
     transformer.inverseFourierTransform();
 
 }
 
 
-void whitenSpectrum(MultidimArray<double> &Min,
-                    MultidimArray<double> &Mout,
+void whitenSpectrum(MultidimArray<DOUBLE> &Min,
+                    MultidimArray<DOUBLE> &Mout,
                     int spectrum_type,
                     bool leave_origin_intact)
 {
 
-    MultidimArray<double> spectrum;
+    MultidimArray<DOUBLE> spectrum;
     getSpectrum(Min,spectrum,spectrum_type);
     Mout=Min;
     divideBySpectrum(Mout,spectrum,leave_origin_intact);
 
 }
 
-void adaptSpectrum(MultidimArray<double> &Min,
-                   MultidimArray<double> &Mout,
-                   const MultidimArray<double> &spectrum_ref,
+void adaptSpectrum(MultidimArray<DOUBLE> &Min,
+                   MultidimArray<DOUBLE> &Mout,
+                   const MultidimArray<DOUBLE> &spectrum_ref,
                    int spectrum_type,
                    bool leave_origin_intact)
 {
 
-    MultidimArray<double> spectrum;
+    MultidimArray<DOUBLE> spectrum;
     getSpectrum(Min,spectrum,spectrum_type);
     FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY1D(spectrum)
     {
@@ -808,13 +937,12 @@ void adaptSpectrum(MultidimArray<double> &Min,
     }
     Mout=Min;
     multiplyBySpectrum(Mout,spectrum,leave_origin_intact);
-
 }
 
 /** Kullback-Leibner divergence */
-double getKullbackLeibnerDivergence(MultidimArray<Complex > &Fimg,
-		MultidimArray<Complex > &Fref, MultidimArray<double> &sigma2,
-		MultidimArray<double> &p_i, MultidimArray<double> &q_i, int highshell, int lowshell )
+DOUBLE getKullbackLeibnerDivergence(MultidimArray<Complex > &Fimg,
+		MultidimArray<Complex > &Fref, MultidimArray<DOUBLE> &sigma2,
+		MultidimArray<DOUBLE> &p_i, MultidimArray<DOUBLE> &q_i, int highshell, int lowshell )
 {
 	// First check dimensions are OK
 	if (!Fimg.sameShape(Fref))
@@ -835,8 +963,8 @@ double getKullbackLeibnerDivergence(MultidimArray<Complex > &Fimg,
 	MultidimArray<int> histogram;
 	int histogram_size = 101;
 	int histogram_origin = histogram_size / 2;
-	double sigma_max = 10.;
-	double histogram_factor = histogram_origin / sigma_max;
+	DOUBLE sigma_max = 10.;
+	DOUBLE histogram_factor = histogram_origin / sigma_max;
 	histogram.initZeros(histogram_size);
 
 	// This way this will work in both 2D and 3D
@@ -846,9 +974,9 @@ double getKullbackLeibnerDivergence(MultidimArray<Complex > &Fimg,
 		if (ires >= lowshell && ires <= highshell)
 		{
 			// Use FT of masked image for noise estimation!
-			double diff_real = (DIRECT_A3D_ELEM(Fref, k, i, j)).real - (DIRECT_A3D_ELEM(Fimg, k, i, j)).real;
-			double diff_imag = (DIRECT_A3D_ELEM(Fref, k, i, j)).imag - (DIRECT_A3D_ELEM(Fimg, k, i, j)).imag;
-			double sigma = sqrt(DIRECT_A1D_ELEM(sigma2, ires));
+			DOUBLE diff_real = (DIRECT_A3D_ELEM(Fref, k, i, j)).real - (DIRECT_A3D_ELEM(Fimg, k, i, j)).real;
+			DOUBLE diff_imag = (DIRECT_A3D_ELEM(Fref, k, i, j)).imag - (DIRECT_A3D_ELEM(Fimg, k, i, j)).imag;
+			DOUBLE sigma = sqrt(DIRECT_A1D_ELEM(sigma2, ires));
 
 			// Divide by standard deviation to normalise all the difference
 			diff_real /= sigma;
@@ -878,35 +1006,35 @@ double getKullbackLeibnerDivergence(MultidimArray<Complex > &Fimg,
 	}
 
 	// Normalise the histogram and the discretised analytical Gaussian
-	double norm = (double)histogram.sum();
-	double gaussnorm = 0.;
+	DOUBLE norm = (DOUBLE)histogram.sum();
+	DOUBLE gaussnorm = 0.;
 	for (int i = 0; i < histogram_size; i++)
 	{
-		double x = (double)i / histogram_factor;
+		DOUBLE x = (DOUBLE)i / histogram_factor;
 		gaussnorm += gaussian1D(x - sigma_max, 1. , 0.);
 	}
 
 	// Now calculate the actual Kullback-Leibner divergence
-	double kl_divergence = 0.;
+	DOUBLE kl_divergence = 0.;
 	p_i.resize(histogram_size);
 	q_i.resize(histogram_size);
 	for (int i = 0; i < histogram_size; i++)
 	{
 		// Data distribution
-		p_i(i) = (double)histogram(i) / norm;
+		p_i(i) = (DOUBLE)histogram(i) / norm;
 		// Theoretical distribution
-		double x = (double)i / histogram_factor;
+		DOUBLE x = (DOUBLE)i / histogram_factor;
 		q_i(i) = gaussian1D(x - sigma_max, 1. , 0.) / gaussnorm;
 
 		if (p_i(i) > 0.)
 			kl_divergence += p_i(i) * log (p_i(i) / q_i(i));
 	}
-	kl_divergence /= (double)histogram_size;
+	kl_divergence /= (DOUBLE)histogram_size;
 
 	return kl_divergence;
 
 }
-void resizeMap(MultidimArray<double > &img, int newsize)
+void resizeMap(MultidimArray<DOUBLE > &img, int newsize)
 {
 
 	FourierTransformer transformer;
@@ -921,86 +1049,12 @@ void resizeMap(MultidimArray<double > &img, int newsize)
 
 }
 
-void correctMapForMTF(MultidimArray<Complex > &FT, int ori_size, FileName &fn_mtf)
-{
-
-	MetaDataTable MDmtf;
-
-	if (!fn_mtf.isStarFile())
-		REPORT_ERROR("correctMapForMTF ERROR: input MTF file is not a STAR file.");
-
-	MDmtf.read(fn_mtf);
-	MultidimArray<double> mtf_resol, mtf_value;
-	mtf_resol.resize(MDmtf.numberOfObjects());
-	mtf_value.resize(mtf_resol);
-
-	int i =0;
-	FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDmtf)
-	{
-		MDmtf.getValue(EMDL_RESOLUTION_INVPIXEL, DIRECT_A1D_ELEM(mtf_resol, i) ); // resolution needs to be given in 1/pix
-		MDmtf.getValue(EMDL_POSTPROCESS_MTF_VALUE, DIRECT_A1D_ELEM(mtf_value, i) );
-		if (DIRECT_A1D_ELEM(mtf_value, i) < 1e-10)
-		{
-			std::cerr << " i= " << i <<  " mtf_value[i]= " << DIRECT_A1D_ELEM(mtf_value, i) << std::endl;
-			REPORT_ERROR("Postprocessing::sharpenMap ERROR: zero or negative values encountered in MTF curve!");
-		}
-		i++;
-	}
-
-    double xsize = (double)ori_size;
-    FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(FT)
-    {
-    	int r2 = kp * kp + ip * ip + jp * jp;
-    	double res = sqrt((double)r2)/xsize; // get resolution in 1/pixel
-		if (res < 0.5 )
-		{
-
-			// Find the suitable MTF value
-			int i_0 = 0;
-			for (int ii = 0; ii < XSIZE(mtf_resol); ii++)
-			{
-				if (DIRECT_A1D_ELEM(mtf_resol, ii) > res)
-					break;
-				i_0 = ii;
-			}
-			// linear interpolation: y = y_0 + (y_1 - y_0)*(x-x_0)/(x1_x0)
-			double mtf;
-			double x_0 = DIRECT_A1D_ELEM(mtf_resol, i_0);
-			if (i_0 == MULTIDIM_SIZE(mtf_resol) - 1 || i_0 == 0) // check boundaries of the array
-				mtf = DIRECT_A1D_ELEM(mtf_value, i_0);
-			else
-			{
-				double x_1 = DIRECT_A1D_ELEM(mtf_resol, i_0 + 1);
-				double y_0 = DIRECT_A1D_ELEM(mtf_value, i_0);
-				double y_1 = DIRECT_A1D_ELEM(mtf_value, i_0 + 1);
-				mtf = y_0 + (y_1 - y_0)*(res - x_0)/(x_1 - x_0);
-			}
-
-			// Divide Fourier component by the MTF
-			DIRECT_A3D_ELEM(FT, k, i, j) /= mtf;
-		}
-    }
-
-
-
-}
-
-void correctMapForMTF(MultidimArray<double > &img, FileName &fn_mtf)
-{
-	FourierTransformer transformer;
-	MultidimArray<Complex > FT;
-	transformer.FourierTransform(img, FT, false);
-	correctMapForMTF(FT, XSIZE(img), fn_mtf);
-	transformer.inverseFourierTransform();
-
-}
-
-void applyBFactorToMap(MultidimArray<Complex > &FT, int ori_size, double bfactor, double angpix)
+void applyBFactorToMap(MultidimArray<Complex > &FT, int ori_size, DOUBLE bfactor, DOUBLE angpix)
 {
 	FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(FT)
 	{
     	int r2 = kp * kp + ip * ip + jp * jp;
-    	double res = sqrt((double)r2)/(ori_size * angpix); // get resolution in 1/Angstrom
+    	DOUBLE res = sqrt((DOUBLE)r2)/(ori_size * angpix); // get resolution in 1/Angstrom
     	if (res <= 1. / (angpix * 2.) ) // Apply B-factor sharpening until Nyquist, then low-pass filter later on (with a soft edge)
 		{
     		DIRECT_A3D_ELEM(FT, k, i, j) *= exp( -(bfactor / 4.)  * res * res);
@@ -1012,7 +1066,7 @@ void applyBFactorToMap(MultidimArray<Complex > &FT, int ori_size, double bfactor
 	}
 }
 
-void applyBFactorToMap(MultidimArray<double > &img, double bfactor, double angpix)
+void applyBFactorToMap(MultidimArray<DOUBLE > &img, DOUBLE bfactor, DOUBLE angpix)
 {
 
 	FourierTransformer transformer;
@@ -1024,7 +1078,7 @@ void applyBFactorToMap(MultidimArray<double > &img, double bfactor, double angpi
 
 
 void lowPassFilterMap(MultidimArray<Complex > &FT, int ori_size,
-		double low_pass, double angpix, int filter_edge_width, bool do_highpass_instead)
+		DOUBLE low_pass, DOUBLE angpix, int filter_edge_width, bool do_highpass_instead)
 {
 
 	// Which resolution shell is the filter?
@@ -1032,15 +1086,15 @@ void lowPassFilterMap(MultidimArray<Complex > &FT, int ori_size,
 	int filter_edge_halfwidth = filter_edge_width / 2;
 
 	// Soft-edge: from 1 shell less to one shell more:
-	double edge_low = XMIPP_MAX(0., (ires_filter - filter_edge_halfwidth) / (double)ori_size); // in 1/pix
-	double edge_high = XMIPP_MIN(XSIZE(FT), (ires_filter + filter_edge_halfwidth) / (double)ori_size); // in 1/pix
-	double edge_width = edge_high - edge_low;
+	DOUBLE edge_low = XMIPP_MAX(0., (ires_filter - filter_edge_halfwidth) / (DOUBLE)ori_size); // in 1/pix
+	DOUBLE edge_high = XMIPP_MIN(XSIZE(FT), (ires_filter + filter_edge_halfwidth) / (DOUBLE)ori_size); // in 1/pix
+	DOUBLE edge_width = edge_high - edge_low;
 
 	// Put a raised cosine from edge_low to edge_high
 	FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(FT)
 	{
     	int r2 = kp * kp + ip * ip + jp * jp;
-    	double res = sqrt((double)r2)/ori_size; // get resolution in 1/pixel
+    	DOUBLE res = sqrt((DOUBLE)r2)/ori_size; // get resolution in 1/pixel
 
     	if (do_highpass_instead)
     	{
@@ -1065,7 +1119,7 @@ void lowPassFilterMap(MultidimArray<Complex > &FT, int ori_size,
 
 }
 
-void lowPassFilterMap(MultidimArray<double > &img, double low_pass, double angpix, int filter_edge_width)
+void lowPassFilterMap(MultidimArray<DOUBLE > &img, DOUBLE low_pass, DOUBLE angpix, int filter_edge_width)
 {
 	FourierTransformer transformer;
 	MultidimArray<Complex > FT;
@@ -1107,7 +1161,7 @@ void lowPassFilterMap(MultidimArray<double > &img, double low_pass, double angpi
 
 }
 
-void highPassFilterMap(MultidimArray<double > &img, double low_pass, double angpix, int filter_edge_width)
+void highPassFilterMap(MultidimArray<DOUBLE > &img, DOUBLE low_pass, DOUBLE angpix, int filter_edge_width)
 {
 	FourierTransformer transformer;
 	MultidimArray<Complex > FT;
@@ -1118,29 +1172,29 @@ void highPassFilterMap(MultidimArray<double > &img, double low_pass, double angp
 
 
 
-void applyBeamTilt(const MultidimArray<Complex > &Fin, MultidimArray<Complex > &Fout, double beamtilt_x, double beamtilt_y,
-		double wavelength, double Cs, double angpix, int ori_size)
+void applyBeamTilt(const MultidimArray<Complex > &Fin, MultidimArray<Complex > &Fout, DOUBLE beamtilt_x, DOUBLE beamtilt_y,
+		DOUBLE wavelength, DOUBLE Cs, DOUBLE angpix, int ori_size)
 {
 
 	Fout = Fin;
 	selfApplyBeamTilt(Fout, beamtilt_x, beamtilt_y, wavelength, Cs, angpix, ori_size);
 }
 
-void selfApplyBeamTilt(MultidimArray<Complex > &Fimg, double beamtilt_x, double beamtilt_y,
-		double wavelength, double Cs, double angpix, int ori_size)
+void selfApplyBeamTilt(MultidimArray<Complex > &Fimg, DOUBLE beamtilt_x, DOUBLE beamtilt_y,
+		DOUBLE wavelength, DOUBLE Cs, DOUBLE angpix, int ori_size)
 {
 	if (Fimg.getDim() != 2)
 		REPORT_ERROR("applyBeamTilt can only be done on 2D Fourier Transforms!");
 
-	double boxsize = angpix * ori_size;
-	double factor = 0.360 * Cs * 10000000 * wavelength * wavelength / (boxsize * boxsize * boxsize);
+	DOUBLE boxsize = angpix * ori_size;
+	DOUBLE factor = 0.360 * Cs * 10000000 * wavelength * wavelength / (boxsize * boxsize * boxsize);
 	FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM2D(Fimg)
 	{
-		double delta_phase = factor * (ip * ip + jp * jp) * (ip * beamtilt_y + jp * beamtilt_x);
-		double realval = DIRECT_A2D_ELEM(Fimg, i, j).real;
-		double imagval = DIRECT_A2D_ELEM(Fimg, i, j).imag;
-		double mag = sqrt(realval * realval + imagval * imagval);
-		double phas = atan2(imagval, realval) + DEG2RAD(delta_phase); // apply phase shift!
+		DOUBLE delta_phase = factor * (ip * ip + jp * jp) * (ip * beamtilt_y + jp * beamtilt_x);
+		DOUBLE realval = DIRECT_A2D_ELEM(Fimg, i, j).real;
+		DOUBLE imagval = DIRECT_A2D_ELEM(Fimg, i, j).imag;
+		DOUBLE mag = sqrt(realval * realval + imagval * imagval);
+		DOUBLE phas = atan2(imagval, realval) + DEG2RAD(delta_phase); // apply phase shift!
 		realval = mag * cos(phas);
 		imagval = mag * sin(phas);
 		DIRECT_A2D_ELEM(Fimg, i, j) = Complex(realval, imagval);
diff --git a/src/fftw.h b/src/fftw.h
index dba92ee..53b725d 100644
--- a/src/fftw.h
+++ b/src/fftw.h
@@ -51,7 +51,6 @@
 #include "src/funcs.h"
 #include "src/tabfuncs.h"
 #include "src/complex.h"
-#include "src/metadata_table.h"
 
 /** @defgroup FourierW FFTW Fourier transforms
   * @ingroup DataLibrary
@@ -122,7 +121,7 @@
  * FourierTransformer transformer;
  * MultidimArray< Complex > Vfft;
  * transformer.FourierTransform(V(),Vfft,false);
- * MultidimArray<double> Vmag;
+ * MultidimArray<DOUBLE> Vmag;
  * Vmag.resize(Vfft);
  * FOR_ALL_ELEMENTS_IN_ARRAY3D(Vmag)
  *     Vmag(k,i,j)=20*log10(abs(Vfft(k,i,j)));
@@ -132,7 +131,7 @@ class FourierTransformer
 {
 public:
     /** Real array, in fact a pointer to the user array is stored. */
-    MultidimArray<double> *fReal;
+    MultidimArray<DOUBLE> *fReal;
 
      /** Complex array, in fact a pointer to the user array is stored. */
     MultidimArray<Complex > *fComplex;
@@ -140,11 +139,19 @@ public:
     /** Fourier array  */
     MultidimArray< Complex > fFourier;
 
-    /* fftw Forawrd plan */
+#ifdef FLOAT_PRECISION
+    /* fftw Forward plan */
+    fftwf_plan fPlanForward;
+
+    /* fftw Backward plan */
+    fftwf_plan fPlanBackward;
+#else
+    /* fftw Forward plan */
     fftw_plan fPlanForward;
 
     /* fftw Backward plan */
     fftw_plan fPlanBackward;
+#endif
 
     /* number of threads*/
     int nthreads;
@@ -238,7 +245,7 @@ public:
         void getFourierCopy(T& V) {
             V.resize(fFourier);
             memcpy(MULTIDIM_ARRAY(V),MULTIDIM_ARRAY(fFourier),
-                MULTIDIM_SIZE(fFourier)*2*sizeof(double));
+                MULTIDIM_SIZE(fFourier)*2*sizeof(DOUBLE));
         }
 
     /** Return a complete Fourier transform (two halves).
@@ -321,10 +328,10 @@ public:
 
 // Internal methods
 public:
-    /* Pointer to the array of doubles with which the plan was computed */
-    double * dataPtr;
+    /* Pointer to the array of DOUBLEs with which the plan was computed */
+    DOUBLE * dataPtr;
 
-    /* Pointer to the array of complex<double> with which the plan was computed */
+    /* Pointer to the array of complex<DOUBLE> with which the plan was computed */
     Complex * complexDataPtr;
 
     /* Initialise all pointers to NULL */
@@ -351,7 +358,7 @@ public:
     void Transform(int sign);
 
     /** Get the Multidimarray that is being used as input. */
-    const MultidimArray<double> &getReal() const;
+    const MultidimArray<DOUBLE> &getReal() const;
     const MultidimArray<Complex > &getComplex() const;
 
     /** Set a Multidimarray for input.
@@ -359,7 +366,7 @@ public:
         transforms it is not modified, but in backward transforms,
         the result will be stored in img. This means that the size
         of img cannot change between calls. */
-    void setReal(MultidimArray<double> &img);
+    void setReal(MultidimArray<DOUBLE> &img);
 
     /** Set a Multidimarray for input.
         The data of img will be the one of fComplex. In forward
@@ -377,7 +384,7 @@ public:
 };
 
 // Randomize phases beyond the given shell (index)
-void randomizePhasesBeyond(MultidimArray<double> &I, int index);
+void randomizePhasesBeyond(MultidimArray<DOUBLE> &I, int index);
 
 /** Center an array, to have its origin at the origin of the FFTW
  *
@@ -653,7 +660,7 @@ void resizeFourierTransform(MultidimArray<T > &in,
 
 	// Otherwise apply a windowing operation
 	MultidimArray<Complex > Fin;
-	MultidimArray<double> Min;
+	MultidimArray<DOUBLE> Min;
 	FourierTransformer transformer;
 	long int x0, y0, z0, xF, yF, zF;
 	x0 = y0 = z0 = FIRST_XMIPP_INDEX(newdim);
@@ -677,7 +684,7 @@ void resizeFourierTransform(MultidimArray<T > &in,
     	REPORT_ERROR("resizeFourierTransform ERROR: dimension should be 1, 2 or 3!");
     }
 
-	// This is to handle double-valued input arrays
+	// This is to handle DOUBLE-valued input arrays
 	Fin.resize(ZSIZE(in), YSIZE(in), XSIZE(in));
 	FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(in)
 	{
@@ -719,21 +726,27 @@ void resizeFourierTransform(MultidimArray<T > &in,
  */
 void getFSC(MultidimArray< Complex > &FT1,
 		    MultidimArray< Complex > &FT2,
-		    MultidimArray< double > &fsc);
+		    MultidimArray< DOUBLE > &fsc);
 
 /** Fourier-Ring-Correlation between two multidimArrays using FFT
  * @ingroup FourierOperations
  * Simpler I/O than above.
  */
-void getFSC(MultidimArray< double > & m1,
-		    MultidimArray< double > & m2,
-		    MultidimArray< double > &fsc);
+void getFSC(MultidimArray< DOUBLE > & m1,
+		    MultidimArray< DOUBLE > & m2,
+		    MultidimArray< DOUBLE > &fsc);
 
 /** Scale matrix using Fourier transform
  * @ingroup FourierOperations
  * Ydim and Xdim define the output size, Mpmem is the matrix to scale
  */
-//void selfScaleToSizeFourier(long int Ydim, long int Xdim, MultidimArray<double>& Mpmem, int nthreads=1);
+//void selfScaleToSizeFourier(long int Ydim, long int Xdim, MultidimArray<DOUBLE>& Mpmem, int nthreads=1);
+
+// Get precalculated AB-matrices for on-the-fly shift calculations
+void getAbMatricesForShiftImageInFourierTransform(MultidimArray<Complex > &in,
+									MultidimArray<Complex > &out,
+									TabSine &tab_sin, TabCosine &tab_cos,
+									DOUBLE oridim, DOUBLE shift_x, DOUBLE shift_y, DOUBLE shift_z = 0.);
 
 // Shift an image through phase-shifts in its Fourier Transform
 // Note that in and out may be the same array, in that case in is overwritten with the result
@@ -742,7 +755,7 @@ void getFSC(MultidimArray< double > & m1,
 void shiftImageInFourierTransform(MultidimArray<Complex > &in,
 								  MultidimArray<Complex > &out,
 								  TabSine &tab_sin, TabCosine &tab_cos,
-								  double oridim, Matrix1D<double> shift);
+								  DOUBLE oridim, DOUBLE shift_x, DOUBLE shift_y, DOUBLE shift_z = 0.);
 
 // Shift an image through phase-shifts in its Fourier Transform (without tabulated sine and cosine)
 // Note that in and out may be the same array, in that case in is overwritten with the result
@@ -750,7 +763,7 @@ void shiftImageInFourierTransform(MultidimArray<Complex > &in,
 // or both can be in Angstroms
 void shiftImageInFourierTransform(MultidimArray<Complex > &in,
 						          MultidimArray<Complex > &out,
-								  double oridim, Matrix1D<double> shift);
+								  DOUBLE oridim, DOUBLE shift_x, DOUBLE shift_y, DOUBLE shift_z = 0.);
 
 #define POWER_SPECTRUM 0
 #define AMPLITUDE_SPECTRUM 1
@@ -759,32 +772,32 @@ void shiftImageInFourierTransform(MultidimArray<Complex > &in,
  * @ingroup FourierOperations
     i.e. the radial average of the (squared) amplitudes of all Fourier components
 */
-void getSpectrum(MultidimArray<double> &Min,
-                 MultidimArray<double> &spectrum,
+void getSpectrum(MultidimArray<DOUBLE> &Min,
+                 MultidimArray<DOUBLE> &spectrum,
                  int spectrum_type=POWER_SPECTRUM);
 
 /** Divide the input map in Fourier-space by the spectrum provided.
  * @ingroup FourierOperations
     If leave_origin_intact==true, the origin pixel will remain untouched
 */
-void divideBySpectrum(MultidimArray<double> &Min,
-                      MultidimArray<double> &spectrum,
+void divideBySpectrum(MultidimArray<DOUBLE> &Min,
+                      MultidimArray<DOUBLE> &spectrum,
                       bool leave_origin_intact=false);
 
 /** Multiply the input map in Fourier-space by the spectrum provided.
  * @ingroup FourierOperations
     If leave_origin_intact==true, the origin pixel will remain untouched
 */
-void multiplyBySpectrum(MultidimArray<double> &Min,
-                        MultidimArray<double> &spectrum,
+void multiplyBySpectrum(MultidimArray<DOUBLE> &Min,
+                        MultidimArray<DOUBLE> &spectrum,
                         bool leave_origin_intact=false);
 
 /** Perform a whitening of the amplitude/power_class spectrum of a 3D map
  * @ingroup FourierOperations
     If leave_origin_intact==true, the origin pixel will remain untouched
 */
-void whitenSpectrum(MultidimArray<double> &Min,
-                    MultidimArray<double> &Mout,
+void whitenSpectrum(MultidimArray<DOUBLE> &Min,
+                    MultidimArray<DOUBLE> &Mout,
                     int spectrum_type=AMPLITUDE_SPECTRUM,
                     bool leave_origin_intact=false);
 
@@ -792,50 +805,45 @@ void whitenSpectrum(MultidimArray<double> &Min,
  * @ingroup FourierOperations
     If only_amplitudes==true, the amplitude rather than the power_class spectrum will be equalized
 */
-void adaptSpectrum(MultidimArray<double> &Min,
-                   MultidimArray<double> &Mout,
-                   const MultidimArray<double> &spectrum_ref,
+void adaptSpectrum(MultidimArray<DOUBLE> &Min,
+                   MultidimArray<DOUBLE> &Mout,
+                   const MultidimArray<DOUBLE> &spectrum_ref,
                    int spectrum_type=AMPLITUDE_SPECTRUM,
                    bool leave_origin_intact=false);
 
 /** Kullback-Leibner divergence */
-double getKullbackLeibnerDivergence(MultidimArray<Complex > &Fimg,
-		MultidimArray<Complex > &Fref, MultidimArray<double> &sigma2,
-		MultidimArray<double> &p_i, MultidimArray<double> &q_i,
+DOUBLE getKullbackLeibnerDivergence(MultidimArray<Complex > &Fimg,
+		MultidimArray<Complex > &Fref, MultidimArray<DOUBLE> &sigma2,
+		MultidimArray<DOUBLE> &p_i, MultidimArray<DOUBLE> &q_i,
 		int highshell = -1, int lowshell = -1);
 
 
 // Resize a map by windowing it's Fourier Transform
-void resizeMap(MultidimArray<double > &img, int newsize);
-
-
-// Correct a map by its MTF-curve
-void correctMapForMTF(MultidimArray<Complex > &FT, int ori_size, FileName &fn_mtf);
-void correctMapForMTF(MultidimArray<double > &img, FileName &fn_mtf);
+void resizeMap(MultidimArray<DOUBLE > &img, int newsize);
 
 // Apply a B-factor to a map (given it's Fourier transform)
-void applyBFactorToMap(MultidimArray<Complex > &FT, int ori_size, double bfactor, double angpix);
+void applyBFactorToMap(MultidimArray<Complex > &FT, int ori_size, DOUBLE bfactor, DOUBLE angpix);
 
 // Apply a B-factor to a map (given it's real-space array)
-void applyBFactorToMap(MultidimArray<double > &img, double bfactor, double angpix);
+void applyBFactorToMap(MultidimArray<DOUBLE > &img, DOUBLE bfactor, DOUBLE angpix);
 
 // Low-pass filter a map (given it's Fourier transform)
 void lowPassFilterMap(MultidimArray<Complex > &FT, int ori_size,
-		double low_pass, double angpix, int filter_edge_width = 2, bool do_highpass_instead = false);
+		DOUBLE low_pass, DOUBLE angpix, int filter_edge_width = 2, bool do_highpass_instead = false);
 
 // Low-pass and high-pass filter a map (given it's real-space array)
-void lowPassFilterMap(MultidimArray<double > &img, double low_pass, double angpix, int filter_edge_width = 2);
-void highPassFilterMap(MultidimArray<double > &img, double low_pass, double angpix, int filter_edge_width = 2);
+void lowPassFilterMap(MultidimArray<DOUBLE > &img, DOUBLE low_pass, DOUBLE angpix, int filter_edge_width = 2);
+void highPassFilterMap(MultidimArray<DOUBLE > &img, DOUBLE low_pass, DOUBLE angpix, int filter_edge_width = 2);
 
 /*
  *  Beamtilt x and y are given in mradians
  *  Wavelength in Angstrom, Cs in mm
  *  Phase shifts caused by the beamtilt will be calculated and applied to Fimg
  */
-void selfApplyBeamTilt(MultidimArray<Complex > &Fimg, double beamtilt_x, double beamtilt_y,
-		double wavelength, double Cs, double angpix, int ori_size);
+void selfApplyBeamTilt(MultidimArray<Complex > &Fimg, DOUBLE beamtilt_x, DOUBLE beamtilt_y,
+		DOUBLE wavelength, DOUBLE Cs, DOUBLE angpix, int ori_size);
 
-void applyBeamTilt(const MultidimArray<Complex > &Fin, MultidimArray<Complex > &Fout, double beamtilt_x, double beamtilt_y,
-		double wavelength, double Cs, double angpix, int ori_size);
+void applyBeamTilt(const MultidimArray<Complex > &Fin, MultidimArray<Complex > &Fout, DOUBLE beamtilt_x, DOUBLE beamtilt_y,
+		DOUBLE wavelength, DOUBLE Cs, DOUBLE angpix, int ori_size);
 
 #endif
diff --git a/src/filename.cpp b/src/filename.cpp
index 335e60a..ba97943 100644
--- a/src/filename.cpp
+++ b/src/filename.cpp
@@ -138,7 +138,7 @@ bool FileName::contains(const std::string& str) const
 // Get substring before first instance of str
 FileName FileName::beforeFirstOf(const std::string& str) const
 {
-    int point = find_first_of(str);
+    int point = find(str);
     if (point > -1)
         return substr(0, point);
     else
@@ -148,7 +148,7 @@ FileName FileName::beforeFirstOf(const std::string& str) const
 // Get substring before last instance of str
 FileName FileName::beforeLastOf(const std::string& str) const
 {
-    int point = find_last_of(str);
+    int point = rfind(str);
     if (point > -1)
         return substr(0, point);
     else
@@ -158,7 +158,7 @@ FileName FileName::beforeLastOf(const std::string& str) const
 // Get substring after first instance of str
 FileName FileName::afterFirstOf(const std::string& str) const
 {
-    int point = find_first_of(str);
+    int point = find(str);
     if (point > -1)
         return substr(point + 1);
     else
@@ -168,7 +168,7 @@ FileName FileName::afterFirstOf(const std::string& str) const
 // Get substring after last instance of str
 FileName FileName::afterLastOf(const std::string& str) const
 {
-    int point = find_last_of(str);
+    int point = rfind(str);
     if (point > -1)
         return substr(point + 1);
     else
@@ -249,26 +249,7 @@ FileName FileName::withoutExtension() const
 // Insert before extension .................................................
 FileName FileName::insertBeforeExtension(const std::string &str) const
 {
-    int point = -1;
-    bool done = false;
-    do
-    {
-        point = find(".", point + 1);
-        if (point == -1)
-        {
-            point = length();
-            done = true;
-        }
-        else if (point == length() - 1)
-            done = true;
-        else if ((*this)[point+1] == '.' || (*this)[point+1] == '/')
-            done = false;
-        else
-            done = true;
-    }
-    while (!done);
-    FileName retval = *this;
-    return retval.insert(point, str);
+    return  withoutExtension() + str + "." + getExtension();
 }
 
 // Remove an extension wherever it is ......................................
diff --git a/src/funcs.cpp b/src/funcs.cpp
index eb28058..318fd87 100644
--- a/src/funcs.cpp
+++ b/src/funcs.cpp
@@ -55,7 +55,7 @@
 #include <fstream>
 #include <typeinfo>
 
-void fitStraightLine(std::vector<fit_point2D> &points, double &slope, double &intercept, double &corr_coeff)
+void fitStraightLine(const std::vector<fit_point2D> &points, DOUBLE &slope, DOUBLE &intercept, DOUBLE &corr_coeff)
 {
 	// From: http://mathworld.wolfram.com/LeastSquaresFitting.html
 	// ss_xx = Sum_i x_i^2 - n ave_x^2
@@ -64,12 +64,12 @@ void fitStraightLine(std::vector<fit_point2D> &points, double &slope, double &in
 	// slope = xx_xy / ss_xx
 	// intercept = ave_y - slope * ave_x
 	// corr_coeff = ss_xy^2 / (ss_xx * ss_yy)
-	double ss_xy = 0.;
-	double ss_xx = 0.;
-	double ss_yy = 0.;
-	double ave_x = 0.;
-	double ave_y = 0.;
-	double sum_w = 0.;
+	DOUBLE ss_xy = 0.;
+	DOUBLE ss_xx = 0.;
+	DOUBLE ss_yy = 0.;
+	DOUBLE ave_x = 0.;
+	DOUBLE ave_y = 0.;
+	DOUBLE sum_w = 0.;
 	for (int i = 0; i < points.size(); i++)
 	{
 		ave_x += points[i].w * points[i].x;
@@ -87,15 +87,63 @@ void fitStraightLine(std::vector<fit_point2D> &points, double &slope, double &in
 
 	//std::cerr << " ss_xx= " << ss_xx << " ss_yy= " << ss_yy << " ss_xy= " << ss_xy << std::endl;
 	//std::cerr << " sum_w= " << sum_w << " ave_x= " << ave_x << " ave_y= " << ave_y << std::endl;
-	slope = ss_xy / ss_xx;
-	intercept = ave_y - slope * ave_x;
-	corr_coeff = ss_xy * ss_xy / (ss_xx * ss_yy);
+	if (ss_xx > 0.)
+	{
+		slope = ss_xy / ss_xx;
+		intercept = ave_y - slope * ave_x;
+		corr_coeff = ss_xy * ss_xy / (ss_xx * ss_yy);
+	}
+	else
+	{
+		intercept = slope = corr_coeff = 0.;
+	}
 }
 
+void fitLeastSquaresPlane(const std::vector<fit_point3D> & points, DOUBLE &plane_a, DOUBLE &plane_b, DOUBLE &plane_c)
+{
+    DOUBLE  D = 0;
+    DOUBLE  E = 0;
+    DOUBLE  F = 0;
+    DOUBLE  G = 0;
+    DOUBLE  H = 0;
+    DOUBLE  I = 0;
+    DOUBLE  J = 0;
+    DOUBLE  K = 0;
+    DOUBLE  L = 0;
+    DOUBLE  W2 = 0;
+    DOUBLE  error = 0;
+    DOUBLE  denom = 0;
+
+    for (int i = 0; i < points.size(); i++)
+    {
+        W2 = points[i].w * points[i].w;
+        D += points[i].x * points[i].x * W2 ;
+        E += points[i].x * points[i].y * W2 ;
+        F += points[i].x * W2 ;
+        G += points[i].y * points[i].y * W2 ;
+        H += points[i].y * W2 ;
+        I += 1 * W2 ;
+        J += points[i].x * points[i].z * W2 ;
+        K += points[i].y * points[i].z * W2 ;
+        L += points[i].z * W2 ;
+    }
+
+    denom = F * F * G - 2 * E * F * H + D * H * H + E * E * I - D * G * I;
+
+    // X axis slope
+    plane_a = (H * H * J - G * I * J + E * I * K + F * G * L - H * (F * K + E * L)) / denom;
+    // Y axis slope
+    plane_b = (E * I * J + F * F * K - D * I * K + D * H * L - F * (H * J + E * L)) / denom;
+    // Z axis intercept
+    plane_c = (F * G * J - E * H * J - E * F * K + D * H * K + E * E * L - D * G * L) / denom;
+}
+
+
+
 /* Value of a blob --------------------------------------------------------- */
-double kaiser_value(double r, double a, double alpha, int m)
+DOUBLE kaiser_value(DOUBLE r, DOUBLE a, DOUBLE alpha, int m)
 {
-    double rda, rdas, arg, w;
+    DOUBLE rda, rdas, arg, w;
     rda = r / a;
     rdas = rda * rda;
     if (rdas <= 1.0)
@@ -142,9 +190,9 @@ double kaiser_value(double r, double a, double alpha, int m)
 /* Value of line integral through Kaiser-Bessel radial function
    (n >=2 dimensions) at distance s from center of function.
    Parameter m = 0, 1, or 2. */
-double kaiser_proj(double s, double a, double alpha, int m)
+DOUBLE kaiser_proj(DOUBLE s, DOUBLE a, DOUBLE alpha, int m)
 {
-    double sda, sdas, w, arg, p;
+    DOUBLE sda, sdas, w, arg, p;
     sda = s / a;
     sdas = sda * sda;
     w = 1.0 - sdas;
@@ -181,9 +229,9 @@ double kaiser_proj(double s, double a, double alpha, int m)
     return p;
 }
 /* Fourier value of a blob ------------------------------------------------- */
-double kaiser_Fourier_value(double w, double a, double alpha, int m)
+DOUBLE kaiser_Fourier_value(DOUBLE w, DOUBLE a, DOUBLE alpha, int m)
 {
-    double sigma = sqrt(ABS(alpha * alpha - (2. * PI * a * w) * (2. * PI * a * w)));
+    DOUBLE sigma = sqrt(ABS(alpha * alpha - (2. * PI * a * w) * (2. * PI * a * w)));
     if (m == 2)
     {
         if (2.*PI*a*w > alpha)
@@ -206,9 +254,9 @@ double kaiser_Fourier_value(double w, double a, double alpha, int m)
     	REPORT_ERROR("m out of range in kaiser_Fourier_value()");
 }
 /* Volume integral of a blob ----------------------------------------------- */
-double  basvolume(double a, double alpha, int m, int n)
+DOUBLE  basvolume(DOUBLE a, DOUBLE alpha, int m, int n)
 {
-    double  hn, tpi, v;
+    DOUBLE  hn, tpi, v;
     hn = 0.5 * n;
     tpi = 2.0 * PI;
     if (alpha == 0.0)
@@ -225,14 +273,14 @@ double  basvolume(double a, double alpha, int m, int n)
         else                        /* n odd                                */
             v = pow(tpi / alpha, hn) * i_nph(n / 2 + m, alpha) / i_n(m, alpha);
     }
-    return v * pow(a, (double)n);
+    return v * pow(a, (DOUBLE)n);
 }
 /* Bessel function I_n (x),  n = 0, 1, 2, ...
  Use ONLY for small values of n     */
-double i_n(int n, double x)
+DOUBLE i_n(int n, DOUBLE x)
 {
     int i;
-    double i_ns1, i_n, i_np1;
+    DOUBLE i_ns1, i_n, i_np1;
     if (n == 0)   return bessi0(x);
     if (n == 1)   return bessi1(x);
     if (x == 0.0) return 0.0;
@@ -247,11 +295,11 @@ double i_n(int n, double x)
     return i_n;
 }
 /*.....Bessel function I_(n+1/2) (x),  n = 0, 1, 2, ..........................*/
-double i_nph(int n, double x)
+DOUBLE i_nph(int n, DOUBLE x)
 {
     int i;
-    double r2dpix;
-    double i_ns1, i_n, i_np1;
+    DOUBLE r2dpix;
+    DOUBLE i_ns1, i_n, i_np1;
     if (x == 0.0) return 0.0;
     r2dpix = sqrt(2.0 / (PI * x));
     i_ns1 = r2dpix * cosh(x);
@@ -265,10 +313,10 @@ double i_nph(int n, double x)
     return i_n;
 }
 /*....Limit (z->0) of (1/z)^n I_n(z)..........................................*/
-double in_zeroarg(int n)
+DOUBLE in_zeroarg(int n)
 {
     int i;
-    double fact;
+    DOUBLE fact;
     fact = 1.0;
     for (i = 1; i <= n; i++)
     {
@@ -277,10 +325,10 @@ double in_zeroarg(int n)
     return fact;
 }
 /*.......Limit (z->0) of (1/z)^(n+1/2) I_(n+1/2) (z)..........................*/
-double inph_zeroarg(int n)
+DOUBLE inph_zeroarg(int n)
 {
     int i;
-    double fact;
+    DOUBLE fact;
     fact = 1.0;
     for (i = 1; i <= n; i++)
     {
@@ -289,46 +337,46 @@ double inph_zeroarg(int n)
     return fact*sqrt(2.0 / PI);
 }
 /* Zero freq --------------------------------------------------------------- */
-double blob_freq_zero(struct blobtype b)
+DOUBLE blob_freq_zero(struct blobtype b)
 {
     return sqrt(b.alpha*b.alpha + 6.9879*6.9879) / (2*PI*b.radius);
 }
 /* Attenuation ------------------------------------------------------------- */
-double blob_att(double w, struct blobtype b)
+DOUBLE blob_att(DOUBLE w, struct blobtype b)
 {
     return blob_Fourier_val(w, b) / blob_Fourier_val(0, b);
 }
 /* Number of operations ---------------------------------------------------- */
-double blob_ops(double w, struct blobtype b)
+DOUBLE blob_ops(DOUBLE w, struct blobtype b)
 {
     return pow(b.alpha*b.alpha + 6.9879*6.9879, 1.5) / b.radius;
 }
 
 /* Gaussian value ---------------------------------------------------------- */
-double gaussian1D(double x, double sigma, double mu)
+DOUBLE gaussian1D(DOUBLE x, DOUBLE sigma, DOUBLE mu)
 {
     x -= mu;
     return 1 / sqrt(2*PI*sigma*sigma)*exp(-0.5*((x / sigma)*(x / sigma)));
 }
 
 /* t-student value -------------------------------------------------------- */
-double tstudent1D(double x, double df, double sigma, double mu)
+DOUBLE tstudent1D(DOUBLE x, DOUBLE df, DOUBLE sigma, DOUBLE mu)
 {
     x -= mu;
-    double norm = exp(gammln((df+1.)/2.)) / exp(gammln(df/2.));
+    DOUBLE norm = exp(gammln((df+1.)/2.)) / exp(gammln(df/2.));
     norm /= sqrt(df*PI*sigma*sigma);
     return norm * pow((1 + (x/sigma)*(x/sigma)/df),-((df+1.)/2.));
 
 }
 
-double gaussian2D(double x, double y, double sigmaX, double sigmaY,
-                  double ang, double muX, double muY)
+DOUBLE gaussian2D(DOUBLE x, DOUBLE y, DOUBLE sigmaX, DOUBLE sigmaY,
+                  DOUBLE ang, DOUBLE muX, DOUBLE muY)
 {
     // Express x,y in the gaussian internal coordinates
     x -= muX;
     y -= muY;
-    double xp = cos(ang) * x + sin(ang) * y;
-    double yp = -sin(ang) * x + cos(ang) * y;
+    DOUBLE xp = cos(ang) * x + sin(ang) * y;
+    DOUBLE yp = -sin(ang) * x + cos(ang) * y;
 
     // Now evaluate
     return 1 / sqrt(2*PI*sigmaX*sigmaY)*exp(-0.5*((xp / sigmaX)*(xp / sigmaX) +
@@ -336,36 +384,36 @@ double gaussian2D(double x, double y, double sigmaX, double sigmaY,
 }
 
 /* ICDF Gaussian ----------------------------------------------------------- */
-double icdf_gauss(double p)
+DOUBLE icdf_gauss(DOUBLE p)
 {
-    const double c[] =
+    const DOUBLE c[] =
         {
             2.515517, 0.802853, 0.010328
         };
-    const double d[] =
+    const DOUBLE d[] =
         {
             1.432788, 0.189269, 0.001308
         };
     if (p < 0.5)
     {
         // F^-1(p) = - G^-1(p)
-        double t=sqrt(-2.0*log(p));
-        double z=t - ((c[2]*t + c[1])*t + c[0]) /
+        DOUBLE t=sqrt(-2.0*log(p));
+        DOUBLE z=t - ((c[2]*t + c[1])*t + c[0]) /
                  (((d[2]*t + d[1])*t + d[0])*t + 1.0);
         return -z;
     }
     else
     {
         // F^-1(p) = G^-1(1-p)
-        double t=sqrt(-2.0*log(1-p));
-        double z=t - ((c[2]*t + c[1])*t + c[0]) /
+        DOUBLE t=sqrt(-2.0*log(1-p));
+        DOUBLE z=t - ((c[2]*t + c[1])*t + c[0]) /
                  (((d[2]*t + d[1])*t + d[0])*t + 1.0);
         return z;
     }
 }
 
 /* CDF Gaussian ------------------------------------------------------------ */
-double cdf_gauss(double x)
+DOUBLE cdf_gauss(DOUBLE x)
 {
     return 0.5 * (1. + erf(x/sqrt(2.)));
 }
@@ -411,17 +459,17 @@ arithmetic   domain     # trials      peak         rms
 Cephes Math Library Release 2.8:  June, 2000
 Copyright 1984, 1987, 1995, 2000 by Stephen L. Moshier
 *************************************************************************/
-double cdf_tstudent(int k, double t)
-{
-    double EPS=5E-16;
-    double result;
-    double x;
-    double rk;
-    double z;
-    double f;
-    double tz;
-    double p;
-    double xsqk;
+DOUBLE cdf_tstudent(int k, DOUBLE t)
+{
+    DOUBLE EPS=5E-16;
+    DOUBLE result;
+    DOUBLE x;
+    DOUBLE rk;
+    DOUBLE z;
+    DOUBLE f;
+    DOUBLE tz;
+    DOUBLE p;
+    DOUBLE xsqk;
     int j;
 
     if ( t==0 )
@@ -488,17 +536,17 @@ double cdf_tstudent(int k, double t)
 
 /* Snedecor's F ------------------------------------------------------------ */
 // http://en.wikipedia.org/wiki/F-distribution
-double cdf_FSnedecor(int d1, int d2, double x)
+DOUBLE cdf_FSnedecor(int d1, int d2, DOUBLE x)
 {
     return betai(0.5*d1,0.5*d2,(d1*x)/(d1*x+d2));
 }
 
-double icdf_FSnedecor(int d1, int d2, double p)
+DOUBLE icdf_FSnedecor(int d1, int d2, DOUBLE p)
 {
-    double xl=0, xr=1e6;
-    double pl=cdf_FSnedecor(d1,d2,xl);
-    double pr=cdf_FSnedecor(d1,d2,xr);
-    double xm, pm;
+    DOUBLE xl=0, xr=1e6;
+    DOUBLE pl=cdf_FSnedecor(d1,d2,xl);
+    DOUBLE pr=cdf_FSnedecor(d1,d2,xr);
+    DOUBLE xm, pm;
     do
     {
         xm=(xl+xr)*0.5;
@@ -518,77 +566,70 @@ double icdf_FSnedecor(int d1, int d2, double p)
     return xm;
 }
 
-/* Random functions -------------------------------------------------------- */
-int idum;
-
 // Uniform distribution ....................................................
-void  init_random_generator(int seed)
+void init_random_generator(int seed)
 {
-    idum = -1;
-    ran1(&idum);
-    if (seed != -1)
-    {
-        // Prevent seeds larger than 65000
-        seed %=0xffff;
-        for (int i = 0; i < seed; i++)
-            ran1(&idum);
-    }
+    if (seed < 0)
+    	randomize_random_generator();
+    else
+    	srand(static_cast <unsigned> (seed) );
 }
 
-void  randomize_random_generator()
+void randomize_random_generator()
 {
-    static  unsigned int seed;
-    int rand_return;
-
-    srand(seed);
-    rand_return = rand();
-
-    time_t t;
-    time(&t);
-    rand_return = abs(rand_return);
-    idum = (-(int)(t % 10000)
-            - (int)(rand_return % 10000));
-    ran1(&idum);
-    seed = (unsigned int)rand_return;
+	srand(static_cast <unsigned> (time(NULL)) );
 }
 
-float rnd_unif()
-{
-    return ran1(&idum);
-}
 float rnd_unif(float a, float b)
 {
-    if (a == b)
-        return a;
-    else
-        return a + (b - a)*ran1(&idum);
-}
 
-// t-distribution
-float rnd_student_t(double nu)
-{
-    return tdev(nu, &idum);
-}
-float rnd_student_t(double nu, float a, float b)
-{
-    if (b == 0)
+
+	if (a == b)
         return a;
     else
-        return b*tdev(nu, &idum) + a;
+        return a + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(b-a)));
 }
 
 // Gaussian distribution ...................................................
-float rnd_gaus()
+float rnd_gaus(float mu, float sigma)
 {
-    return gasdev(&idum);
+  float U1, U2, W, mult;
+  static float X1, X2;
+  static int call = 0;
+
+  if (sigma == 0)
+	  return mu;
+
+  if (call == 1)
+  {
+      call = !call;
+      return (mu + sigma * (float) X2);
+  }
+
+  do
+  {
+      U1 = -1 + ((float) rand () / RAND_MAX) * 2;
+      U2 = -1 + ((float) rand () / RAND_MAX) * 2;
+      W = pow (U1, 2) + pow (U2, 2);
+  }
+  while (W >= 1 || W == 0);
+
+  mult = sqrt ((-2 * log (W)) / W);
+  X1 = U1 * mult;
+  X2 = U2 * mult;
+
+  call = !call;
+
+  return (mu + sigma * (float) X1);
+
 }
-float rnd_gaus(float a, float b)
+
+float rnd_student_t(DOUBLE nu, float mu, float sigma)
 {
-    if (b == 0)
-        return a;
-    else
-        return b*gasdev(&idum) + a;
+	REPORT_ERROR("rnd_student_t currently not implemented!");
 }
+
+
 float gaus_within_x0(float x0, float mean, float stddev)
 {
     float z0 = (x0 - mean) / stddev;
@@ -710,7 +751,7 @@ float rnd_log(float a, float b)
 /* Log2 -------------------------------------------------------------------- */
 // Does not work with xlc compiler
 #ifndef __xlC__
-double log2(double value)
+DOUBLE log2(DOUBLE value)
 {
     return 3.32192809488736*log10(value);
     // log10(value)/log10(2)
diff --git a/src/funcs.h b/src/funcs.h
index 1534f01..f822e30 100644
--- a/src/funcs.h
+++ b/src/funcs.h
@@ -62,19 +62,34 @@
 
 #define FILENAMENUMBERLENGTH 6
 
-/** Structure of the points to do least-squares fitting
+/** Structure of the points to do least-squares straight-line fitting
  */
 struct fit_point2D
 {
     /// x coordinate
-    double x;
+    DOUBLE x;
     /// y coordinate (assumed to be a function of x)
-    double y;
+    DOUBLE y;
     /// Weight of the point in the Least-Squares problem
-    double w;
+    DOUBLE w;
 };
 
-void fitStraightLine(std::vector<fit_point2D> &points, double &slope, double &intercept, double &corr_coeff);
+void fitStraightLine(const std::vector<fit_point2D> &points, DOUBLE &slope, DOUBLE &intercept, DOUBLE &corr_coeff);
+
+/** Structure of the points to do least-squares plane fitting
+ */
+struct fit_point3D
+{
+    /// x coordinate
+    DOUBLE x;
+    /// y coordinate
+    DOUBLE y;
+    /// z coordinate (assumed to be a function of x,y)
+    DOUBLE z;
+    /// Weight of the point in the Least-Squares problem
+    DOUBLE w;
+};
+void fitLeastSquaresPlane(const std::vector<fit_point3D> & points, DOUBLE &plane_a, DOUBLE &plane_b, DOUBLE &plane_c);
 
 /* ========================================================================= */
 /* BLOBS                                                                     */
@@ -113,8 +128,8 @@ void fitStraightLine(std::vector<fit_point2D> &points, double &slope, double &in
          blob.order  = 2;                       // Order of the Bessel function
          blob.alpha  = textToFloat(argv[1]);    // Smoothness parameter
 
-         double M=blob_Fourier_val (0, blob);
-         for (double w=0; w<=2; w += 0.05)
+         DOUBLE M=blob_Fourier_val (0, blob);
+         for (DOUBLE w=0; w<=2; w += 0.05)
             std::cout << w << " " <<  blob_Fourier_val (w, blob)/M << std::endl;
          return 0;
       }
@@ -123,13 +138,13 @@ void fitStraightLine(std::vector<fit_point2D> &points, double &slope, double &in
 struct blobtype
 {
     /// Spatial radius in Universal System units
-    double radius;
+    DOUBLE radius;
 
     /// Derivation order and Bessel function order
     int   order;
 
     /// Smoothness parameter
-    double alpha;
+    DOUBLE alpha;
 };
 
 // Blob value --------------------------------------------------------------
@@ -144,13 +159,13 @@ struct blobtype
     \\ Ex:
     @code
     struct blobtype blob; blob.radius = 2; blob.order = 2; blob.alpha = 3.6;
-    Matrix1D<double> v=vectorR3(1,1,1);
+    Matrix1D<DOUBLE> v=vectorR3(1,1,1);
     std::cout << "Blob value at (1,1,1) = " << blob_val(v.mod(),blob) << std::endl;
     @endcode */
 #define blob_val(r, blob) kaiser_value(r, blob.radius, blob.alpha, blob.order)
 
 /** Function actually computing the blob value. */
-double kaiser_value(double r, double a, double alpha, int m);
+DOUBLE kaiser_value(DOUBLE r, DOUBLE a, DOUBLE alpha, int m);
 
 // Blob projection ---------------------------------------------------------
 /** Blob projection.
@@ -165,14 +180,14 @@ double kaiser_value(double r, double a, double alpha, int m);
     \\ Ex:
     @code
     struct blobtype blob; blob.radius = 2; blob.order = 2; blob.alpha = 3.6;
-    Matrix1D<double> v=vectorR3(1,1,1);
+    Matrix1D<DOUBLE> v=vectorR3(1,1,1);
     std::cout << "Blob line integral through (1,1,1) = " << blob_proj(v.mod(),blob)
          << std::endl;
     @endcode */
 #define blob_proj(r, blob) kaiser_proj(r, blob.radius, blob.alpha, blob.order)
 
 /** Function actually computing the blob projection. */
-double kaiser_proj(double r, double a, double alpha, int m);
+DOUBLE kaiser_proj(DOUBLE r, DOUBLE a, DOUBLE alpha, int m);
 
 /** Fourier transform of a blob.
     This function returns the value of the Fourier transform of the blob
@@ -186,56 +201,56 @@ double kaiser_proj(double r, double a, double alpha, int m);
     kaiser_Fourier_value(w, blob.radius, blob.alpha, blob.order)
 
 /** Function actually computing the blob Fourier transform. */
-double kaiser_Fourier_value(double w, double a, double alpha, int m);
+DOUBLE kaiser_Fourier_value(DOUBLE w, DOUBLE a, DOUBLE alpha, int m);
 
 /** Formula for a volume integral of a blob (n is the blob dimension) */
 #define blob_mass(blob) \
     basvolume(blob.radius, blob.alpha, blob.order,3)
 
 /** Function actually computing the blob integral */
-double  basvolume(double a, double alpha, int m, int n);
+DOUBLE  basvolume(DOUBLE a, DOUBLE alpha, int m, int n);
 
 /** Limit (z->0) of (1/z)^n I_n(z) (needed by basvolume)*/
-double in_zeroarg(int n);
+DOUBLE in_zeroarg(int n);
 
 /** Limit (z->0) of (1/z)^(n+1/2) I_(n+1/2) (z) (needed by basvolume)*/
-double inph_zeroarg(int n);
+DOUBLE inph_zeroarg(int n);
 
 /** Bessel function I_(n+1/2) (x),  n = 0, 1, 2, ... */
-double i_nph(int n, double x);
+DOUBLE i_nph(int n, DOUBLE x);
 
 /** Bessel function I_n (x),  n = 0, 1, 2, ...
  Use ONLY for small values of n */
-double i_n(int n, double x);
+DOUBLE i_n(int n, DOUBLE x);
 
 /** Blob pole.
     This is the normalized frequency at which the blob goes to 0. */
-double blob_freq_zero(struct blobtype b);
+DOUBLE blob_freq_zero(struct blobtype b);
 
 /** Attenuation of a blob.
     The Fourier transform of the blob at w is the Fourier transform at w=0
     multiplied by the attenuation. This is the value returned. Remind that
     the frequency must be normalized by the sampling rate. Ie, Tm*w(cont) */
-double blob_att(double w, struct blobtype b);
+DOUBLE blob_att(DOUBLE w, struct blobtype b);
 
 /** Number of operations for a blob.
     This is a number proportional to the number of operations that ART
     would need to make a reconstruction with this blob. */
-double blob_ops(double w, struct blobtype b);
+DOUBLE blob_ops(DOUBLE w, struct blobtype b);
 
 /** 1D gaussian value
  *
  * This function returns the value of a univariate gaussian function at the
  * point x.
  */
-double gaussian1D(double x, double sigma, double mu = 0);
+DOUBLE gaussian1D(DOUBLE x, DOUBLE sigma, DOUBLE mu = 0);
 
 /** 1D t-student value
  *
  * This function returns the value of a univariate t-student function at the
  * point x, and with df degrees of freedom
  */
-double tstudent1D(double x, double df, double sigma, double mu = 0);
+DOUBLE tstudent1D(DOUBLE x, DOUBLE df, DOUBLE sigma, DOUBLE mu = 0);
 
 /** Inverse Cumulative distribution function for a Gaussian
  *
@@ -244,14 +259,14 @@ double tstudent1D(double x, double df, double sigma, double mu = 0);
  * The function employs an fast approximation to z which is valid up to 1e-4.
  * See http://www.johndcook.com/normal_cdf_inverse.html
  */
-double icdf_gauss(double p);
+DOUBLE icdf_gauss(DOUBLE p);
 
 /** Cumulative distribution function for a Gaussian
  *
  * This function returns the value of the CDF of a univariate gaussian function at the
  * point x.
  */
-double cdf_gauss(double x);
+DOUBLE cdf_gauss(DOUBLE x);
 
 /** Cumulative distribution function for a t-distribution
  *
@@ -259,7 +274,7 @@ double cdf_gauss(double x);
  * with k degrees of freedom  at the point t.
  *  Adapted by Sjors from: http://www.alglib.net/specialfunctions/distributions/student.php
  */
-double cdf_tstudent(int k, double t);
+DOUBLE cdf_tstudent(int k, DOUBLE t);
 
 /** Cumulative distribution function for a Snedecor's F-distribution.
  *
@@ -267,7 +282,7 @@ double cdf_tstudent(int k, double t);
  * F-distribution
  * with d1, d2 degrees of freedom  at the point x.
  */
-double cdf_FSnedecor(int d1, int d2, double x);
+DOUBLE cdf_FSnedecor(int d1, int d2, DOUBLE x);
 
 /** Inverse Cumulative distribution function for a Snedecor's F-distribution.
  *
@@ -276,7 +291,7 @@ double cdf_FSnedecor(int d1, int d2, double x);
  * with d1, d2 degrees of freedom with probability p, i.e., it returns
  * x such that CDF(d1,d2,x)=p
  */
-double icdf_FSnedecor(int d1, int d2, double p);
+DOUBLE icdf_FSnedecor(int d1, int d2, DOUBLE p);
 
 /** 2D gaussian value
  *
@@ -285,20 +300,20 @@ double icdf_FSnedecor(int d1, int d2, double p);
  * (counter-clockwise) radians (the angle is positive when measured from the
  * universal X to the gaussian X). X and Y are supposed to be independent.
  */
-double gaussian2D(double x,
-                  double y,
-                  double sigmaX,
-                  double sigmaY,
-                  double ang,
-                  double muX = 0,
-                  double muY = 0);
+DOUBLE gaussian2D(DOUBLE x,
+                  DOUBLE y,
+                  DOUBLE sigmaX,
+                  DOUBLE sigmaY,
+                  DOUBLE ang,
+                  DOUBLE muX = 0,
+                  DOUBLE muY = 0);
 //@}
 
 /** Compute the logarithm in base 2
  */
 // Does not work with xlc compiler
 #ifndef __xlC__
-double log2(double value);
+DOUBLE log2(DOUBLE value);
 #endif
 //@}
 
@@ -343,15 +358,6 @@ void init_random_generator(int seed = -1);
  */
 void randomize_random_generator();
 
-/** Produce a uniform random number between 0 and 1
- *
- * @code
- * std::cout << "This random number should be between 0 and 1: " << rnd_unif()
- * << std::endl;
- * @endcode
- */
-float rnd_unif();
-
 /** Produce a uniform random number between a and b
  *
  * @code
@@ -359,43 +365,25 @@ float rnd_unif();
  * << std::endl;
  * @endcode
  */
-float rnd_unif(float a, float b);
+float rnd_unif(float a = 0., float b = 1.);
 
-/** Produce a t-distributed random number with mean 0 and standard deviation 1 and nu degrees of freedom
+/** Produce a gaussian random number with mean a and standard deviation b
  *
  * @code
- * std::cout << "This random number should follow t(0,1) with 3 degrees of freedon: " << rnd_student_t(3.)
+ * std::cout << "This random number should follow N(1,4): " << rnd_gaus(1,2)
  * << std::endl;
  * @endcode
  */
-float rnd_student_t(double nu);
+float rnd_gaus(float mu = 0., float sigma = 1.);
 
-/** Produce a gaussian random number with mean a and standard deviation b and nu degrees of freedom
+/** Produce a gaussian random number with mean mu and standard deviation sigma and nu degrees of freedom
  *
  * @code
  * std::cout << "This random number should follow t(1,4) with 3 d.o.f.: " << rnd_gaus(3,1,2)
  * << std::endl;
  * @endcode
  */
-float rnd_student_t(double nu, float a, float b);
-
-/** Produce a gaussian random number with mean 0 and standard deviation 1
- *
- * @code
- * std::cout << "This random number should follow N(0,1): " << rnd_gaus()
- * << std::endl;
- * @endcode
- */
-float rnd_gaus();
-
-/** Produce a gaussian random number with mean a and standard deviation b
- *
- * @code
- * std::cout << "This random number should follow N(1,4): " << rnd_gaus(1,2)
- * << std::endl;
- * @endcode
- */
-float rnd_gaus(float a, float b);
+float rnd_student_t(DOUBLE nu, float mu = 0., float sigma = 1.);
 
 /** Gaussian area from -x0 to x0
  *
@@ -490,7 +478,6 @@ float rnd_log(float a, float b);
  */
 void swapbytes(char* v, unsigned long n);
 
-
 //@}
 
 //@}
diff --git a/src/gcc_version.h b/src/gcc_version.h
index 6400a59..15ed07d 100644
--- a/src/gcc_version.h
+++ b/src/gcc_version.h
@@ -61,7 +61,6 @@
 #include <sstream>
 #endif
 
-
 #endif
 
 
diff --git a/src/gui_entries.h b/src/gui_entries.h
index 72a21f6..d4582fe 100644
--- a/src/gui_entries.h
+++ b/src/gui_entries.h
@@ -64,15 +64,17 @@
 //version-1.0 #define GUI_BUTTON_COLOR (fl_rgb_color(200,255,100))
 //version-1.1 #define GUI_BUTTON_COLOR (fl_rgb_color(50,150,250))
 //version-1.2 #define GUI_BUTTON_COLOR (fl_rgb_color(155,150,255))
+//version-1.3 #define GUI_BUTTON_COLOR (fl_rgb_color(50, 200, 50))
 //devel-version
-#define GUI_BUTTON_COLOR (fl_rgb_color(50, 200, 50))
-#define GUI_BUTTON_DARK_COLOR (fl_rgb_color(0, 155, 0))
+#define GUI_BUTTON_COLOR (fl_rgb_color(60, 180, 155))
+#define GUI_BUTTON_DARK_COLOR (fl_rgb_color(45, 135, 120))
 //possible?#define GUI_BUTTON_COLOR (fl_rgb_color(50, 200, 255))
 //version-1.0 #define GUI_RUNBUTTON_COLOR (fl_rgb_color(255,155,0))
 //version-1.1 #define GUI_RUNBUTTON_COLOR (fl_rgb_color(255,50,50))
 //version-1.2 #define GUI_RUNBUTTON_COLOR (fl_rgb_color(205,53,100))
+//version-1.3 #define GUI_RUNBUTTON_COLOR (fl_rgb_color(255,80,80))
 //devel-version
-#define GUI_RUNBUTTON_COLOR (fl_rgb_color(255,80,80))
+#define GUI_RUNBUTTON_COLOR (fl_rgb_color(205,40,150))
 //possible?#define GUI_RUNBUTTON_COLOR (fl_rgb_color(205,0,155))
 #
 #define GUI_BACKGROUND_COLOR (fl_rgb_color(240,240,240))
diff --git a/src/gui_jobwindow.cpp b/src/gui_jobwindow.cpp
index 46c9817..c0c95d0 100644
--- a/src/gui_jobwindow.cpp
+++ b/src/gui_jobwindow.cpp
@@ -27,7 +27,20 @@ RelionJobWindow::RelionJobWindow(int nr_tabs, bool _has_mpi, bool _has_thread, b
 	has_mpi = _has_mpi;
 	has_thread = _has_thread;
 
-    // Set up tabs
+	// Check for environment variable RELION_QSUB_TEMPLATE
+	char * my_minimum_dedicated = getenv ("RELION_MINIMUM_DEDICATED");
+	minimum_nr_dedicated = (my_minimum_dedicated == NULL) ? DEFAULTMININIMUMDEDICATED : textToInteger(my_minimum_dedicated);
+
+	char * my_allow_change_dedicated = getenv ("RELION_ALLOW_CHANGE_MINIMUM_DEDICATED");
+	if (my_allow_change_dedicated == NULL)
+		do_allow_change_minimum_dedicated = DEFAULTMININIMUMDEDICATED;
+	else
+	{
+		int check_allow =  textToInteger(my_allow_change_dedicated);
+		do_allow_change_minimum_dedicated = (check_allow == 0) ? false : true;
+	}
+
+	// Set up tabs
     if (nr_tabs >= 1) // there is always the running tab, which is not counted on the input nr_tabs!
     {
     	tabs = new Fl_Tabs(x, current_y, w, h - MENUHEIGHT);
@@ -183,7 +196,9 @@ XXXcommandXXX = relion command + arguments; \n \
 XXXqueueXXX = The queue name; \n \
 XXXmpinodesXXX = The number of MPI nodes; \n \
 XXXthreadsXXX = The number of threads; \n \
-XXXcoresXXX = The number of MPI nodes * nr_threads; \n \
+XXXcoresXXX = XXXmpinodesXXX * XXXthreadsXXX; \n \
+XXXdedicatedXXX = The minimum number of dedicated cores on each node; \n \
+XXXnodesXXX = The number of requested nodes = CEIL(XXXcoresXXX / XXXdedicatedXXX); \n \
 If these options are not enough for your standard jobs, you may define two extra variables: XXXextra1XXX and XXXextra2XXX \
 Their help text is set by the environment variables RELION_QSUB_EXTRA1 and RELION_QSUB_EXTRA2 \
 For example, setenv RELION_QSUB_EXTRA1 \"Max number of hours in queue\" will result in an additional (text) ein the GUI \
@@ -191,10 +206,17 @@ Any variables XXXextra1XXX in the template script will be replaced by the corres
 Likewise, default values for the extra entries can be set through environment variables RELION_QSUB_EXTRA1_DEFAULT and  RELION_QSUB_EXTRA2_DEFAULT. \
 But note that (unlike all other entries in the GUI) the extra values are not remembered from one run to the other.");
 
+	min_dedicated.place(current_y, "Minimum dedicated cores per node:", minimum_nr_dedicated, 1, 64, 1, "Minimum number of dedicated cores that need to be requested on each node. This is useful to force the queue to fill up entire nodes of a given size.");
+	if (do_allow_change_minimum_dedicated)
+		min_dedicated.deactivate(false);
+	else
+		min_dedicated.deactivate(true);
 
 	queue_group->end();
 	do_queue.cb_menu_i(); // This is to make the default effective
 
+
+
     // Add a little spacer
     current_y += STEPY/2;
 
@@ -298,11 +320,24 @@ void RelionJobWindow::saveJobSubmissionScript(std::string newfilename, std::stri
 	    fl_alert("Error reading from file \'%s\':\n%s.", qsubscript.getValue().c_str(), strerror(errno));
 
 	// default to a single thread
+	int nmpi = nr_mpi.getValue();
 	int nthr = (has_thread) ? nr_threads.getValue() : 1;
+	int ncores = nr_mpi.getValue() * nthr;
+	int ndedi = min_dedicated.getValue();
+	float fnodes = (float)ncores / (float)ndedi;
+	int nnodes = CEIL(fnodes);
+	if (fmod(fnodes, 1) > 0)
+	{
+		std:: cout << std::endl;
+		std::cout << " Warning! You're using " << nmpi << " MPI processes with " << nthr << " threads each (i.e. " << ncores << " cores), while asking for " << nnodes << " nodes with " << ndedi << " cores." << std::endl;
+		std::cout << " It is more efficient to make the number of cores (i.e. mpi*threads) a multiple of the minimum number of dedicated cores per node " << std::endl;
+	}
 
-	replaceStringAll(textbuf, "XXXmpinodesXXX", floatToString(nr_mpi.getValue()) );
+	replaceStringAll(textbuf, "XXXmpinodesXXX", floatToString(nmpi) );
 	replaceStringAll(textbuf, "XXXthreadsXXX", floatToString(nthr) );
-	replaceStringAll(textbuf, "XXXcoresXXX", floatToString(nr_mpi.getValue() * nthr) );
+	replaceStringAll(textbuf, "XXXcoresXXX", floatToString(ncores) );
+	replaceStringAll(textbuf, "XXXdedicatedXXX", floatToString(ndedi) );
+	replaceStringAll(textbuf, "XXXnodesXXX", floatToString(nnodes) );
 	replaceStringAll(textbuf, "XXXnameXXX", outputname);
 	replaceStringAll(textbuf, "XXXerrfileXXX", outputname + ".err");
 	replaceStringAll(textbuf, "XXXoutfileXXX", outputname + ".out");
@@ -493,7 +528,7 @@ CtffindJobWindow::CtffindJobWindow() : RelionJobWindow(3, HAS_MPI, HAS_NOT_THREA
 	tab1->label("I/O");
 	resetHeight();
 
-	mic_names.place(current_y, "Input micrographs for CTF:", "micrographs_selected.star", "Input micrographs (*.{star,mrc})", "STAR file with the filenames of all micrographs on which to run CTFFIND3, OR a unix-type wildcard to the filenames of the micrograph(s) (e.g. Micrographs/*.mrc).\
+	mic_names.place(current_y, "Input micrographs for CTF:", "micrographs_selected.star", "Input micrographs (*.{star,mrc})", "STAR file with the filenames of all micrographs on which to run CTFFIND, OR a unix-type wildcard to the filenames of the micrograph(s) (e.g. Micrographs/*.mrc).\
 Note that the micrographs should be in a subdirectory (e.g. called Micrographs/) of the project directory, i.e. the directory from where you are launching the GUI. \
 If this is not the case, then make a symbolic link inside the project directory to the directory where your micrographs are stored.");
 
@@ -503,14 +538,14 @@ If this is not the case, then make a symbolic link inside the project directory
 	current_y += STEPY/2;
 
 	// Check for environment variable RELION_QSUB_TEMPLATE
-	char * default_location = getenv ("RELION_CTFFIND3_EXECUTABLE");
+	char * default_location = getenv ("RELION_CTFFIND_EXECUTABLE");
 	if (default_location == NULL)
 	{
 		char mydefault[]=DEFAULTCTFFINDLOCATION;
 		default_location=mydefault;
 	}
 
-	fn_ctffind3_exe.place(current_y, "CTFFIND3 executable:", default_location, "*.exe", "Location of the CTFFIND3 executable. You can control the default of this field by setting environment variable RELION_CTFFIND3_EXECUTABLE, or by editing the first few lines in src/gui_jobwindow.h and recompile the code.");
+	fn_ctffind_exe.place(current_y, "CTFFIND executable:", default_location, "*.exe", "Location of the CTFFIND executable. You can control the default of this field by setting environment variable RELION_CTFFIND_EXECUTABLE, or by editing the first few lines in src/gui_jobwindow.h and recompile the code.");
 
 	ctf_win.place(current_y, "Estimate CTF on window size (pix) ", -1, -16, 4096, 16, "If a positive value is given, a squared window of this size at the center of the micrograph will be used to estimate the CTF. This may be useful to exclude parts of the micrograph that are unsuitable for CTF estimation, e.g. the labels at the edge of phtographic film. \n \n The original micrograph will be used (i.e. this option will be ignored) if a negative value is given.");
 
@@ -532,22 +567,22 @@ If this is not the case, then make a symbolic link inside the project directory
 	tab2->end();
 
 	tab3->begin();
-	tab3->label("CTFFIND3");
+	tab3->label("CTFFIND");
 	resetHeight();
 
-	box.place(current_y, "FFT box size (pix):", 512, 64, 1024, 8, "CTFFIND3's Box parameter");
+	box.place(current_y, "FFT box size (pix):", 512, 64, 1024, 8, "CTFFIND's Box parameter");
 
-	resmin.place(current_y, "Minimum resolution (A):", 100, 10, 200, 10, "CTFFIND3's ResMin parameter");
+	resmin.place(current_y, "Minimum resolution (A):", 30, 10, 200, 10, "CTFFIND's ResMin parameter");
 
-	resmax.place(current_y, "Maximum resolution (A):", 7, 1, 20, 1, "CTFFIND3's ResMax parameter");
+	resmax.place(current_y, "Maximum resolution (A):", 5, 1, 20, 1, "CTFFIND's ResMax parameter");
 
-	dfmin.place(current_y, "Minimum defocus value (A):", 5000, 0, 25000, 1000, "CTFFIND3's dFMin parameter");
+	dfmin.place(current_y, "Minimum defocus value (A):", 5000, 0, 25000, 1000, "CTFFIND's dFMin parameter");
 
-	dfmax.place(current_y, "Maximum defocus value (A):", 50000, 20000, 100000, 1000, "CTFFIND3's dFMax parameter");
+	dfmax.place(current_y, "Maximum defocus value (A):", 50000, 20000, 100000, 1000, "CTFFIND's dFMax parameter");
 
-	dfstep.place(current_y, "Defocus step size (A):", 500, 200, 2000, 100,"CTFFIND3's FStep parameter");
+	dfstep.place(current_y, "Defocus step size (A):", 500, 200, 2000, 100,"CTFFIND's FStep parameter");
 
-	dast.place(current_y, "Amount of astigmatism (A):", 0, 0, 2000, 100,"CTFFIND3's dAst parameter");
+	dast.place(current_y, "Amount of astigmatism (A):", 100, 0, 2000, 100,"CTFFIND's dAst parameter");
 
 	tab3->end();
 
@@ -573,7 +608,7 @@ void CtffindJobWindow::write(std::string fn)
 	dfmax.writeValue(fh);
 	dfstep.writeValue(fh);
 	dast.writeValue(fh);
-	fn_ctffind3_exe.writeValue(fh);
+	fn_ctffind_exe.writeValue(fh);
 	ctf_win.writeValue(fh);
 
 	closeWriteFile(fh);
@@ -599,7 +634,7 @@ void CtffindJobWindow::read(std::string fn, bool &_is_continue)
 		dfmax.readValue(fh);
 		dfstep.readValue(fh);
 		dast.readValue(fh);
-		fn_ctffind3_exe.readValue(fh);
+		fn_ctffind_exe.readValue(fh);
 		ctf_win.readValue(fh);
 
 		closeReadFile(fh);
@@ -617,14 +652,14 @@ void CtffindJobWindow::toggle_new_continue(bool _is_continue)
 	kv.deactivate(is_continue);
 	q0.deactivate(is_continue);
 	dstep.deactivate(is_continue);
-	fn_ctffind3_exe.deactivate(is_continue);
+	fn_ctffind_exe.deactivate(is_continue);
 
 	// TODO: check which log files do not have Final values and re-run on those
 	// for that: modify run_ctffind wrapper program
 }
 
 void CtffindJobWindow::getCommands(std::string &outputname, std::vector<std::string> &commands,
-		std::string &final_command, double angpix)
+		std::string &final_command, DOUBLE angpix)
 {
 	commands.clear();
 	std::string command;
@@ -635,7 +670,7 @@ void CtffindJobWindow::getCommands(std::string &outputname, std::vector<std::str
 
 
 	// Calculate magnification from user-specified pixel size in Angstroms
-	double magn = ROUND((dstep.getValue() * 1e-6) / (angpix * 1e-10));
+	DOUBLE magn = ROUND((dstep.getValue() * 1e-6) / (angpix * 1e-10));
 
 	command += " --i \"" + mic_names.getValue()+"\"";
 	command += " --o \"" + output_star_ctf_mics.getValue()+"\"";
@@ -652,7 +687,7 @@ void CtffindJobWindow::getCommands(std::string &outputname, std::vector<std::str
 	command += " --dFMax " + floatToString(dfmax.getValue());
 	command += " --FStep " + floatToString(dfstep.getValue());
 	command += " --dAst " + floatToString(dast.getValue());
-	command += " --ctffind3_exe " + fn_ctffind3_exe.getValue();
+	command += " --ctffind_exe " + fn_ctffind_exe.getValue();
 
 	if (is_continue)
 		command += " --only_do_unfinished ";
@@ -790,7 +825,7 @@ void ManualpickJobWindow::toggle_new_continue(bool _is_continue)
 }
 
 void ManualpickJobWindow::getCommands(std::string &outputname, std::vector<std::string> &commands, std::string &final_command,
-		double angpix, double particle_diameter)
+		DOUBLE angpix, DOUBLE particle_diameter)
 {
 	commands.clear();
 	std::string command;
@@ -878,6 +913,8 @@ AutopickJobWindow::AutopickJobWindow() : RelionJobWindow(3, HAS_MPI, HAS_NOT_THR
 
 	mindist_autopick.place(current_y, "Minimum inter-particle distance (A):", 100, 0, 1000, 20, "Particles closer together than this distance will be consider to be a single cluster. From each cluster, only one particle will be picked.");
 
+	maxstddevnoise_autopick.place(current_y, "Maximum stddev noise:", 1.1, 0.9, 1.5, 0.02, "This is useful to prevent picking in carbon areas, or areas with big contamination features. Peaks in areas where the background standard deviation in the normalized micrographs is higher than this value will be ignored. Useful values are probably in the range 1.0 to 1.2. Set to -1 to switch off the feature to eliminate peaks due to high background standard deviations.");
+
 	current_y += STEPY/2;
 
 	do_write_fom_maps.place(current_y, "Write FOM maps?", false, "If set to Yes, intermediate probability maps will be written out, which (upon reading them back in) will speed up tremendously the optimization of the threshold and inter-particle distance parameters. However, with this option, one cannot run in parallel, as disc I/O is very heavy with this option set.");
@@ -907,6 +944,7 @@ void AutopickJobWindow::write(std::string fn)
 	do_read_fom_maps.writeValue(fh);
 	threshold_autopick.writeValue(fh);
 	mindist_autopick.writeValue(fh);
+	maxstddevnoise_autopick.writeValue(fh);
 
 	closeWriteFile(fh);
 }
@@ -930,6 +968,7 @@ void AutopickJobWindow::read(std::string fn, bool &_is_continue)
 		do_read_fom_maps.readValue(fh);
 		threshold_autopick.readValue(fh);
 		mindist_autopick.readValue(fh);
+		maxstddevnoise_autopick.readValue(fh);
 
 		closeReadFile(fh);
 		_is_continue = is_continue;
@@ -943,7 +982,7 @@ void AutopickJobWindow::toggle_new_continue(bool _is_continue)
 }
 
 void AutopickJobWindow::getCommands(std::string &outputname, std::vector<std::string> &commands,
-		std::string &final_command, double angpix, double particle_diameter)
+		std::string &final_command, DOUBLE angpix, DOUBLE particle_diameter)
 {
 	commands.clear();
 	std::string command;
@@ -978,7 +1017,7 @@ void AutopickJobWindow::getCommands(std::string &outputname, std::vector<std::st
 
 	command += " --threshold " + floatToString(threshold_autopick.getValue());
 	command += " --min_distance " + floatToString(mindist_autopick.getValue());
-
+	command += " --max_stddev_noise " + floatToString(maxstddevnoise_autopick.getValue());
 
 	// Other arguments
 	command += " " + other_args.getValue();
@@ -1013,7 +1052,7 @@ On disc, movie particles will be distinguished from the average particles by the
 	extract_group->end();
 
 	do_extract.place(current_y, "Extract particles from micrographs?", true, "If set to Yes, particles will be extracted from the micrographs using all selected coordinate files. \
-Niko Grigorieff's program CTFFIND3 will be used for this.", extract_group);
+Niko Grigorieff's program CTFFIND will be used for this.", extract_group);
 
 	extract_group->begin();
 
@@ -1145,7 +1184,7 @@ void ExtractJobWindow::toggle_new_continue(bool _is_continue)
 }
 
 void ExtractJobWindow::getCommands(std::string &outputname, std::vector<std::string> &commands, std::string &final_command,
-		double angpix, double particle_diameter)
+		DOUBLE angpix, DOUBLE particle_diameter)
 {
 	commands.clear();
 	std::string command;
@@ -1171,7 +1210,7 @@ void ExtractJobWindow::getCommands(std::string &outputname, std::vector<std::str
 		}
 
 		// Operate stuff
-		double bg_radius;
+		DOUBLE bg_radius;
 		bg_radius = (particle_diameter / (2. * angpix));
 		if (do_rescale.getValue())
 		{
@@ -1260,7 +1299,7 @@ void SortJobWindow::toggle_new_continue(bool _is_continue)
 }
 
 void SortJobWindow::getCommands(std::string &outputname, std::vector<std::string> &commands,
-		std::string &final_command, double angpix, double particle_diameter)
+		std::string &final_command, DOUBLE angpix, DOUBLE particle_diameter)
 {
 	commands.clear();
 	std::string command;
@@ -1326,6 +1365,13 @@ with X being the iteration from which one continues the previous run.");
 	// Add a little spacer
 	current_y += STEPY/2;
 
+	do_parallel_discio.place(current_y, "Use parallel disc I/O?", true, "If set to Yes, all MPI slaves will read images from disc. \
+Otherwise, only the master will read images and send them through the network to the slaves. Parallel file systems like gluster of fhgfs are good at parallel disc I/O. NFS may break with many slaves reading in parallel.");
+
+
+	// Add a little spacer
+	current_y += STEPY/2;
+
 	nr_classes.place(current_y, "Number of classes:", 1, 1, 50, 1, "The number of classes (K) for a multi-reference refinement. \
 These classes will be made in an unsupervised manner from a single reference by division of the data into random subsets during the first iteration.");
 
@@ -1453,6 +1499,7 @@ void Class2DJobWindow::write(std::string fn)
 	fn_cont.writeValue(fh);
 	fn_img.writeValue(fh);
 	nr_classes.writeValue(fh);
+	do_parallel_discio.writeValue(fh);
 
 	// CTF
 	do_ctf_correction.writeValue(fh);
@@ -1486,6 +1533,7 @@ void Class2DJobWindow::read(std::string fn, bool &_is_continue)
 		fn_cont.readValue(fh);
 		fn_img.readValue(fh);
 		nr_classes.readValue(fh);
+		do_parallel_discio.readValue(fh);
 
 		// CTF
 		do_ctf_correction.readValue(fh);
@@ -1524,7 +1572,7 @@ void Class2DJobWindow::toggle_new_continue(bool _is_continue)
 }
 
 void Class2DJobWindow::getCommands(std::string &outputname, std::vector<std::string> &commands,
-		std::string &final_command, double angpix, double particle_diameter)
+		std::string &final_command, DOUBLE angpix, DOUBLE particle_diameter)
 {
 	commands.clear();
 	std::string command;
@@ -1558,6 +1606,9 @@ void Class2DJobWindow::getCommands(std::string &outputname, std::vector<std::str
 		command += " --particle_diameter " + floatToString(particle_diameter);
 		command += " --angpix " + floatToString(angpix);
 	}
+	// Parallel disc I/O?
+	if (!do_parallel_discio.getValue())
+		command += " --no_parallel_disc_io";
 
 	// CTF stuff
 	if (!is_continue)
@@ -1646,6 +1697,11 @@ with X being the iteration from which one continues the previous run.");
 
 	// Add a little spacer
 	current_y += STEPY/2;
+	do_parallel_discio.place(current_y, "Use parallel disc I/O?", true, "If set to Yes, all MPI slaves will read images from disc. \
+Otherwise, only the master will read images and send them through the network to the slaves. Parallel file systems like gluster of fhgfs are good at parallel disc I/O. NFS may break with many slaves reading in parallel.");
+
+	// Add a little spacer
+	current_y += STEPY/2;
 
 	nr_classes.place(current_y, "Number of classes:", 1, 1, 50, 1, "The number of classes (K) for a multi-reference refinement. \
 These classes will be made in an unsupervised manner from a single reference by division of the data into random subsets during the first iteration.");
@@ -1733,7 +1789,7 @@ Also note that upon restarting, the iteration number continues to be increased,
 The number given here is the TOTAL number of iterations. For example, if 10 iterations have been performed previously and one restarts to perform \
 an additional 5 iterations (for example with a finer angular sampling), then the number given here should be 10+5=15.");
 
-	tau_fudge.place(current_y, "Regularisation parameter T:", 2 , 0.1, 10, 0.1, "Bayes law strictly determines the relative weight between \
+	tau_fudge.place(current_y, "Regularisation parameter T:", 4 , 0.1, 10, 0.1, "Bayes law strictly determines the relative weight between \
 the contribution of the experimental data and the prior. However, in practice one may need to adjust this weight to put slightly more weight on \
 the experimental data to allow optimal results. Values greater than 1 for this regularisation parameter (T in the JMB2011 paper) put more \
 weight on the experimental data. Values around 2-4 have been observed to be useful for 3D refinements, values of 1-2 for 2D refinements. \
@@ -1835,6 +1891,7 @@ void Class3DJobWindow::write(std::string fn)
 	fn_cont.writeValue(fh);
 	fn_img.writeValue(fh);
 	nr_classes.writeValue(fh);
+	do_parallel_discio.writeValue(fh);
 
 	// Reference
 	fn_ref.writeValue(fh);
@@ -1878,6 +1935,7 @@ void Class3DJobWindow::read(std::string fn, bool &_is_continue)
 		fn_cont.readValue(fh);
 		fn_img.readValue(fh);
 		nr_classes.readValue(fh);
+		do_parallel_discio.readValue(fh);
 
 		// Reference
 		fn_ref.readValue(fh);
@@ -1940,7 +1998,7 @@ void Class3DJobWindow::toggle_new_continue(bool _is_continue)
 }
 
 void Class3DJobWindow::getCommands(std::string &outputname, std::vector<std::string> &commands,
-		std::string &final_command, double angpix, double particle_diameter)
+		std::string &final_command, DOUBLE angpix, DOUBLE particle_diameter)
 {
 	commands.clear();
 	std::string command;
@@ -1981,6 +2039,9 @@ void Class3DJobWindow::getCommands(std::string &outputname, std::vector<std::str
 			command += " --ini_high " + floatToString(ini_high.getValue());
 
 	}
+	// Parallel disc I/O?
+	if (!do_parallel_discio.getValue())
+		command += " --no_parallel_disc_io";
 
 	// CTF stuff
 	if (!is_continue)
@@ -2085,6 +2146,13 @@ If they are the same, the program will automatically add a '_ctX' to the output
 with X being the iteration from which one continues the previous run. \n \
 Besides restarting jobs that were somehow stopped before convergence, also use the continue-option after the last iteration to do movie processing.");
 
+	// Add a little spacer
+	current_y += STEPY/2;
+
+	do_parallel_discio.place(current_y, "Use parallel disc I/O?", true, "If set to Yes, all MPI slaves will read images from disc. \
+Otherwise, only the master will read images and send them through the network to the slaves. Parallel file systems like gluster of fhgfs are good at parallel disc I/O. NFS may break with many slaves reading in parallel.");
+
+
 	tab1->end();
 	tab2->begin();
 	tab2->label("Reference");
@@ -2265,6 +2333,7 @@ void Auto3DJobWindow::write(std::string fn)
 	fn_out.writeValue(fh);
 	fn_cont.writeValue(fh);
 	fn_img.writeValue(fh);
+	do_parallel_discio.writeValue(fh);
 
 	// Reference
 	fn_ref.writeValue(fh);
@@ -2310,6 +2379,7 @@ void Auto3DJobWindow::read(std::string fn, bool &_is_continue)
 		fn_out.readValue(fh);
 		fn_cont.readValue(fh);
 		fn_img.readValue(fh);
+		do_parallel_discio.readValue(fh);
 
 		// Reference
 		fn_ref.readValue(fh);
@@ -2386,7 +2456,7 @@ void Auto3DJobWindow::toggle_new_continue(bool _is_continue)
 }
 
 void Auto3DJobWindow::getCommands(std::string &outputname, std::vector<std::string> &commands,
-		std::string &final_command, double angpix, double particle_diameter)
+		std::string &final_command, DOUBLE angpix, DOUBLE particle_diameter)
 {
 	commands.clear();
 	std::string command;
@@ -2427,6 +2497,9 @@ void Auto3DJobWindow::getCommands(std::string &outputname, std::vector<std::stri
 			command += " --ini_high " + floatToString(ini_high.getValue());
 
 	}
+	// Parallel disc I/O?
+	if (!do_parallel_discio.getValue())
+		command += " --no_parallel_disc_io";
 
 	// CTF stuff
 	if (!is_continue)
@@ -2673,7 +2746,7 @@ void PostJobWindow::toggle_new_continue(bool _is_continue)
 	is_continue = _is_continue;
 }
 void PostJobWindow::getCommands(std::string &outputname, std::vector<std::string> &commands, std::string &final_command,
-		double angpix)
+		DOUBLE angpix)
 {
 	commands.clear();
 	std::string command;
@@ -2872,7 +2945,7 @@ void PolishJobWindow::toggle_new_continue(bool _is_continue)
 }
 
 void PolishJobWindow::getCommands(std::string &outputname, std::vector<std::string> &commands, std::string &final_command,
-		double angpix, double particle_diameter, double black_dust, double white_dust)
+		DOUBLE angpix, DOUBLE particle_diameter, DOUBLE black_dust, DOUBLE white_dust)
 {
 	commands.clear();
 	std::string command;
@@ -2914,7 +2987,7 @@ void PolishJobWindow::getCommands(std::string &outputname, std::vector<std::stri
 	command += " --sym " + sym_name.getValue();
 
 	// Normalisation
-	double bg_rad = ROUND(particle_diameter / (2. * angpix));
+	DOUBLE bg_rad = ROUND(particle_diameter / (2. * angpix));
 	command += " --bg_radius " + floatToString(bg_rad);
 	command += " --white_dust " + floatToString(white_dust);
 	command += " --black_dust " + floatToString(black_dust);
@@ -3016,7 +3089,7 @@ void ResmapJobWindow::toggle_new_continue(bool _is_continue)
 }
 
 void ResmapJobWindow::getCommands(std::string &outputname, std::vector<std::string> &commands, std::string &final_command,
-		double angpix)
+		DOUBLE angpix)
 {
 	commands.clear();
 	std::string command;
@@ -3080,18 +3153,22 @@ PublishJobWindow::PublishJobWindow() : RelionJobWindow(2, HAS_NOT_MPI, HAS_NOT_T
 
 	cite_text.place(current_y, "\
 If RELION is useful in your work, please cite us. Relevant papers are:\n \n \
- * The general Bayesian approach (and the first mention of RELION): \n \
+ * General Bayesian approach (and first mention of RELION): \n \
      Scheres (2012) J. Mol. Biol. (PMID: 22100448)	 \n \n\
  * RELION implementation details and the 3D auto-refine procedure: \n \
      Scheres (2012) J. Struct. Biol. (PMID: 23000701)	 \n \n\
- * The gold-standard FSC and the relevance of the 0.143 criterion: \n \
+ * Gold-standard FSC and the relevance of the 0.143 criterion: \n \
      Scheres & Chen (2012) Nat. Meth. (PMID: 22842542)	 \n \n\
- * The movie-processing procedure: \n \
+ * Movie-processing procedure: \n \
      Bai et al. (2013) eLife (PMID: 23427024 )	 \n \n\
- * The correction of mask effects on the FSC curve by randomised phases: \n \
+ * Correction of mask effects on the FSC curve by randomised phases: \n \
      Chen et al. (2013) Ultramicroscopy (PMID: 23872039)	 \n \n\
- * The auto-picking, sorting and particle-polishing: \n \
-     Scheres (2014) in preparation"
+ * Particle-polishing: \n \
+     Scheres (2014) eLife (PMID: 25122622)	 \n \n\
+ * Auto-picking : \n \
+     Scheres (2014) J. Struct. Biol. (PMID: 25486611) \n \n \
+ * Sub-tomogram averaging : \n \
+     Bharat et al. (2015) Structure (submitted). \n \n "
 , GUIWIDTH - WCOL0 - 50, GUIHEIGHT - 150);
 
 	//cite_text.mydisp->textsize(12);
@@ -3103,7 +3180,7 @@ If RELION is useful in your work, please cite us. Relevant papers are:\n \n \
 
 	cite_external_text.place(current_y, "\
 Please also cite the following EXTERNAL programs: \n \n \
-* CTFFIND3 for CTF-estimation: \n \
+* CTFFIND for CTF-estimation: \n \
     Mindell & Grigorieff (2003) J. Mol. Biol. (PMID: 12781660) \n \n\
 * ResMap for local-resolution estimation:  \n\
     Kucukelbir et al. (2014) Nat. Meth. (PMID: 24213166)"
@@ -3123,7 +3200,7 @@ void PublishJobWindow::toggle_new_continue(bool _is_continue)
 void PublishJobWindow::getCommands(std::string &outputname, std::vector<std::string> &commands, std::string &final_command)
 {
 	commands.clear();
-	std::string command = " Sorry, you still need to write your own paper... ;-) ";
+	std::string command = " echo 'Sorry, you still need to write your own paper... ;-)' ";
 
 	commands.push_back(command);
 	outputname = "run_publish";
diff --git a/src/gui_jobwindow.h b/src/gui_jobwindow.h
index 8463dbd..a04845e 100644
--- a/src/gui_jobwindow.h
+++ b/src/gui_jobwindow.h
@@ -27,14 +27,19 @@
 #define HAS_NOT_MPI false
 #define HAS_THREAD true
 #define HAS_NOT_THREAD false
+#define HAS_PARALLEL_DISCIO true
+#define HAS_NOT_PARALLEL_DISCIO false
 #define HAS_RUN true
 #define HAS_NOT_RUN false
 
 #include "src/gui_entries.h"
+
 // Our own defaults at LMB are the hard-coded ones
 #define DEFAULTQSUBLOCATION "/public/EM/RELION/relion/bin/qsub.csh"
-#define DEFAULTCTFFINDLOCATION "/public/EM/CTFFIND/ctffind3.exe"
+#define DEFAULTCTFFINDLOCATION "\"/public/EM/ctffind/ctffind.exe  --omp-num-threads 1 --old-school-input\""
 #define DEFAULTRESMAPLOCATION "/public/EM/ResMap/ResMap-1.1.4-linux64"
+#define DEFAULTMININIMUMDEDICATED 1
+#define DEFAULTALLOWCHANGEMINDEDICATED true
 
 static Fl_Menu_Item sampling_options[] = {
 		      {"30 degrees"},
@@ -49,6 +54,9 @@ static Fl_Menu_Item sampling_options[] = {
 		      {0} // this should be the last entry
 };
 
+static int minimum_nr_dedicated;
+static bool do_allow_change_minimum_dedicated;
+
 class RelionJobWindow : public Fl_Box
 {
 public:
@@ -74,6 +82,7 @@ public:
     BooleanEntry do_queue;
 	AnyEntry queuename;
 	AnyEntry qsub;
+	SliderEntry min_dedicated;
 	FileNameEntry qsubscript;
 	bool have_extra1, have_extra2;
 	AnyEntry qsub_extra1;
@@ -185,7 +194,7 @@ public:
 
 	FileNameEntry mic_names;
 	AnyEntry output_star_ctf_mics;
-	FileNameEntry fn_ctffind3_exe;
+	FileNameEntry fn_ctffind_exe;
 	SliderEntry ctf_win;
 	SliderEntry cs, kv, q0, angpix, dstep, dast;
 	SliderEntry box, resmin, resmax, dfmin, dfmax, dfstep;
@@ -206,7 +215,7 @@ public:
 	void toggle_new_continue(bool is_continue);
 
 	void getCommands(std::string &outputname, std::vector<std::string> &commands,
-			std::string &final_command, double angpix);
+			std::string &final_command, DOUBLE angpix);
 
 };
 
@@ -225,6 +234,7 @@ public:
 	BooleanEntry do_write_fom_maps, do_read_fom_maps;
 	SliderEntry threshold_autopick;
 	SliderEntry mindist_autopick;
+	SliderEntry maxstddevnoise_autopick;
 
 	Fl_Group *autopick_ctf_group;
 
@@ -244,7 +254,7 @@ public:
 	void toggle_new_continue(bool is_continue);
 
 	void getCommands(std::string &outputname, std::vector<std::string> &commands,
-			std::string &final_command, double angpix, double particle_diameter);
+			std::string &final_command, DOUBLE angpix, DOUBLE particle_diameter);
 
 };
 
@@ -286,7 +296,7 @@ public:
 	void toggle_new_continue(bool is_continue);
 
 	void getCommands(std::string &outputname, std::vector<std::string> &commands,
-			std::string &final_command, double angpix, double particle_diameter);
+			std::string &final_command, DOUBLE angpix, DOUBLE particle_diameter);
 
 };
 
@@ -335,7 +345,7 @@ public:
 
 	// Generate the correct commands
 	void getCommands(std::string &outputname, std::vector<std::string> &commands,
-			std::string &final_command, double angpix, double particle_diameter);
+			std::string &final_command, DOUBLE angpix, DOUBLE particle_diameter);
 
 };
 
@@ -368,7 +378,7 @@ public:
 
 	// Generate the correct commands
 	void getCommands(std::string &outputname, std::vector<std::string> &commands,
-			std::string &final_command, double angpix, double particle_diameter);
+			std::string &final_command, DOUBLE angpix, DOUBLE particle_diameter);
 
 };
 
@@ -382,6 +392,7 @@ public:
 	AnyEntry fn_out;
 	FileNameEntry fn_cont;
 	FileNameEntry fn_img;
+	BooleanEntry do_parallel_discio;
 
 	// CTF
 	BooleanEntry do_ctf_correction;
@@ -420,7 +431,7 @@ public:
 
 	// Generate the correct commands
 	void getCommands(std::string &outputname, std::vector<std::string> &commands,
-			std::string &final_command, double angpix, double particle_diameter);
+			std::string &final_command, DOUBLE angpix, DOUBLE particle_diameter);
 
 };
 
@@ -434,6 +445,7 @@ public:
 	FileNameEntry fn_cont;
 	FileNameEntry fn_img;
 	SliderEntry nr_classes;
+	BooleanEntry do_parallel_discio;
 
 	// Reference
 	FileNameEntry fn_ref;
@@ -481,7 +493,7 @@ public:
 
 	// Generate the correct commands
 	void getCommands(std::string &outputname, std::vector<std::string> &commands,
-			std::string &final_command, double angpix, double particle_diameter);
+			std::string &final_command, DOUBLE angpix, DOUBLE particle_diameter);
 
 };
 
@@ -494,6 +506,7 @@ public:
 	AnyEntry fn_out;
 	FileNameEntry fn_cont;
 	FileNameEntry fn_img;
+	BooleanEntry do_parallel_discio;
 
 	// Reference
 	FileNameEntry fn_ref;
@@ -545,7 +558,7 @@ public:
 
 	// Generate the correct commands
 	void getCommands(std::string &outputname, std::vector<std::string> &commands,
-			std::string &final_command, double angpix, double particle_diameter);
+			std::string &final_command, DOUBLE angpix, DOUBLE particle_diameter);
 
 };
 
@@ -595,7 +608,7 @@ public:
 
 	// Generate the correct commands
 	void getCommands(std::string &outputname, std::vector<std::string> &commands, std::string &final_command,
-			double angpix);
+			DOUBLE angpix);
 
 };
 
@@ -638,7 +651,7 @@ public:
 
 	// Generate the correct commands
 	void getCommands(std::string &outputname, std::vector<std::string> &commands, std::string &final_command,
-			double angpix, double particle_diameter, double black_dust, double white_dust);
+			DOUBLE angpix, DOUBLE particle_diameter, DOUBLE black_dust, DOUBLE white_dust);
 };
 
 class ResmapJobWindow : public RelionJobWindow
@@ -675,7 +688,7 @@ public:
 
 	// Generate the correct commands
 	void getCommands(std::string &outputname, std::vector<std::string> &commands, std::string &final_command,
-			double angpix);
+			DOUBLE angpix);
 
 };
 
diff --git a/src/gui_mainwindow.cpp b/src/gui_mainwindow.cpp
index 67d350c..15784d4 100644
--- a/src/gui_mainwindow.cpp
+++ b/src/gui_mainwindow.cpp
@@ -69,8 +69,8 @@ RelionMainWindow::RelionMainWindow(int w, int h, const char* title):Fl_Window(w,
     browser->add("2D classification");
     browser->add("3D classification");
     browser->add("3D auto-refine");
-    browser->add("Post-processing");
     browser->add("Particle polishing");
+    browser->add("Post-processing");
     browser->add("Local-resolution");
     browser->add("Publish!");
 
@@ -136,15 +136,15 @@ RelionMainWindow::RelionMainWindow(int w, int h, const char* title):Fl_Window(w,
     }
     // browse page
     {
+
         browse_grp[9] = new Fl_Group(WCOL0, MENUHEIGHT, 550, 600-MENUHEIGHT);
-    	job_post = new PostJobWindow();
+    	job_polish = new PolishJobWindow();
        	browse_grp[9]->end();
     }
     // browse page
     {
-
         browse_grp[10] = new Fl_Group(WCOL0, MENUHEIGHT, 550, 600-MENUHEIGHT);
-    	job_polish = new PolishJobWindow();
+    	job_post = new PostJobWindow();
        	browse_grp[10]->end();
     }
     // browse page
@@ -288,27 +288,27 @@ void RelionMainWindow::jobCommunicate(bool do_write, bool do_read, bool do_toggl
 	else if (myval == 10)
 	{
 		if (do_write)
-			job_post->write(fn_settings);
+			job_polish->write(fn_settings);
 		if (do_read)
-			job_post->read(fn_settings, is_main_continue);
+			job_polish->read(fn_settings, is_main_continue);
 		if (do_toggle_continue)
-			job_post->toggle_new_continue(is_main_continue);
+			job_polish->toggle_new_continue(is_main_continue);
 		if (do_commandline)
-			job_post->getCommands(outputname, commands, final_command,
-					job_general->angpix.getValue());
+			job_polish->getCommands(outputname, commands, final_command,
+					job_general->angpix.getValue(), job_general->particle_diameter.getValue(),
+					job_extract->black_dust.getValue(), job_extract->white_dust.getValue());
 	}
 	else if (myval == 11)
 	{
 		if (do_write)
-			job_polish->write(fn_settings);
+			job_post->write(fn_settings);
 		if (do_read)
-			job_polish->read(fn_settings, is_main_continue);
+			job_post->read(fn_settings, is_main_continue);
 		if (do_toggle_continue)
-			job_polish->toggle_new_continue(is_main_continue);
+			job_post->toggle_new_continue(is_main_continue);
 		if (do_commandline)
-			job_polish->getCommands(outputname, commands, final_command,
-					job_general->angpix.getValue(), job_general->particle_diameter.getValue(),
-					job_extract->black_dust.getValue(), job_extract->white_dust.getValue());
+			job_post->getCommands(outputname, commands, final_command,
+					job_general->angpix.getValue());
 	}
 	else if (myval == 12)
 	{
@@ -372,6 +372,7 @@ void RelionMainWindow::cb_select_browsegroup_i()
     jobCommunicate(DONT_WRITE, DONT_READ, DO_TOGGLE_CONT, DONT_GET_CL);
 
 }
+
 // Display button call-back functions
 void RelionMainWindow::cb_display(Fl_Widget* o, void* v) {
 
@@ -452,7 +453,7 @@ void RelionMainWindow::cb_run_i()
 		return;
 	}
 
-	std::cerr << "Executing: " << final_command << std::endl;
+	std::cout << "Executing: " << final_command << std::endl;
 	system(final_command.c_str());
 
 	// Deactivate Run button to prevent the user from accidentally submitting many jobs
@@ -511,9 +512,9 @@ void RelionMainWindow::cb_menubar_load_i()
     		browser->value(8);
     	else if  (fn_settings.rfind(".gui_auto3d.") < npos)
     		browser->value(9);
-    	else if  (fn_settings.rfind(".gui_post.") < npos)
-    		browser->value(10);
     	else if  (fn_settings.rfind(".gui_polish.") < npos)
+    		browser->value(10);
+    	else if  (fn_settings.rfind(".gui_post.") < npos)
     		browser->value(11);
     	else if  (fn_settings.rfind(".gui_resmap.") < npos)
     		browser->value(12);
@@ -562,7 +563,7 @@ void RelionMainWindow::cb_menubar_about_i()
 	ShowHelpText *help = new ShowHelpText("\
 RELION is written by Sjors Scheres at the MRC Laboratory of Molecular Biology (scheres at mrc-lmb.cam.ac.uk).\n \
 \n\
-If RELION is useful in your work, please cite us in the contexts as explained under the \"Publish!\" tab, or on the RELION wiki at http://www2.mrc-lmb.cam.ac.uk/relion. \n  \
+If RELION is useful in your work, please cite it in the contexts as explained under the \"Publish!\" tab, or on the RELION wiki at http://www2.mrc-lmb.cam.ac.uk/relion. \n  \
 \n\
 Note that RELION is completely free, open-source software. You can redistribute it and/or modify it for your own purposes, but please do make sure \
 the contribution of Sjors Scheres is acknowledged appropriately. In order to maintain an overview of existing versions, he would also appreciate being \
diff --git a/src/healpix_sampling.cpp b/src/healpix_sampling.cpp
index 25af0a8..0412ae0 100644
--- a/src/healpix_sampling.cpp
+++ b/src/healpix_sampling.cpp
@@ -19,21 +19,21 @@
  ***************************************************************************/
 #include "src/healpix_sampling.h"
 //#define DEBUG_SAMPLING
-
+//#define DEBUG_CHECKSIZES
 void HealpixSampling::clear()
 {
 	is_3D = false;
 	fn_sym = "C1";
 	limit_tilt = psi_step = offset_range = offset_step = 0.;
+	random_perturbation = perturbation_factor = 0.;
 	orientational_prior_mode = NOPRIOR;
 	directions_ipix.clear();
-	directions_angles.clear();
+	rot_angles.clear();
+	tilt_angles.clear();
 	psi_angles.clear();
-	translations.clear();
-	pointer_dir_nonzeroprior.clear();
-	pointer_psi_nonzeroprior.clear();
-	directions_prior.clear();
-	psi_prior.clear();
+	translations_x.clear();
+	translations_y.clear();
+	translations_z.clear();
 	L_repository.clear();
 	R_repository.clear();
 	pgGroup = pgOrder = 0;
@@ -74,8 +74,8 @@ void HealpixSampling::initialise(int prior_mode, int ref_dim, bool _do_3d_trans)
 		SL.read_sym_file(fn_sym);
 
 		// Precalculate (3x3) symmetry matrices
-		Matrix2D<double>  L(4, 4), R(4, 4);
-		Matrix2D<double>  Identity(3,3);
+		Matrix2D<DOUBLE>  L(4, 4), R(4, 4);
+		Matrix2D<DOUBLE>  Identity(3,3);
 		Identity.initIdentity();
 		R_repository.clear();
 		L_repository.clear();
@@ -194,9 +194,9 @@ void HealpixSampling::write(FileName fn_out)
 		MD.clear();
 		MD.setIsList(false);
 		MD.setName("sampling_directions");
-		for (long int idir = 0; idir < NrDirections(0, true); idir++)
+		for (long int idir = 0; idir < NrDirections(); idir++)
 		{
-			double rot, tilt;
+			DOUBLE rot, tilt;
 			getDirection(idir, rot, tilt);
 			MD.addObject();
 			MD.setValue(EMDL_ORIENT_ROT, rot);
@@ -212,65 +212,89 @@ void HealpixSampling::write(FileName fn_out)
 
 }
 
-void HealpixSampling::setTranslations(double _offset_step, double _offset_range)
+void HealpixSampling::setTranslations(DOUBLE _offset_step, DOUBLE _offset_range)
 {
-	translations.clear();
+	translations_x.clear();
+	translations_y.clear();
+	translations_z.clear();
 	if (_offset_step > 0. && _offset_range >= 0.)
 	{
 		offset_step = _offset_step;
 		offset_range = _offset_range;
 	}
+	else
+	{
+		if (!(offset_step > 0.))
+		{
+			std::cerr << " offset_range= " << offset_range << " offset_step= " << offset_step << std::endl;
+			REPORT_ERROR("HealpixSampling::setTranslations BUG %% Trying to set translations with uninitialised offset_step!");
+		}
+	}
 
 	int maxr = CEIL(offset_range / offset_step);
 	for (long int ix = -maxr; ix <= maxr; ix++)
 	{
-		double xoff = ix * offset_step;
+		DOUBLE xoff = ix * offset_step;
 		for (long int iy = -maxr; iy <= maxr; iy++)
 		{
-			double yoff = iy * offset_step;
+			DOUBLE yoff = iy * offset_step;
 
 			if (is_3d_trans)
 			{
 				for (long int iz = -maxr; iz <= maxr; iz++)
 				{
-					double zoff = iz * offset_step;
+					DOUBLE zoff = iz * offset_step;
 					if (xoff*xoff + yoff*yoff + zoff*zoff <= offset_range * offset_range)
-						translations.push_back(vectorR3(xoff, yoff, zoff));
+					{
+						translations_x.push_back(xoff);
+						translations_y.push_back(yoff);
+						translations_z.push_back(zoff);
 
+					}
 				}
 			}
 			else
 			{
 				if (xoff*xoff + yoff*yoff <= offset_range * offset_range)
-					translations.push_back(vectorR2(xoff, yoff));
+				{
+					translations_x.push_back(xoff);
+					translations_y.push_back(yoff);
+				}
 			}
 		}
 	}
 
 #ifdef DEBUG_SETTRANS
 	std::cerr << " is_3d_trans= " << is_3d_trans << std::endl;
-	for (int i = 0; i < translations.size(); i++)
-		std::cerr << " translations[i]= " << translations[i] << std::endl;
+	for (int i = 0; i < translations_x.size(); i++)
+		std::cerr << " translations_x[i]= " << translations_x[i] << std::endl;
 #endif
 
 }
 /* Set only a single translation */
-void HealpixSampling::setOneTranslation(Matrix1D<double> offset)
+void HealpixSampling::addOneTranslation(DOUBLE offset_x, DOUBLE offset_y, DOUBLE offset_z, bool do_clear)
 {
-
-	translations.clear();
-	translations.push_back(offset);
-
+	if (do_clear)
+	{
+		translations_x.clear();
+		translations_y.clear();
+		translations_z.clear();
+	}
+	translations_x.push_back(offset_x);
+	translations_y.push_back(offset_y);
+	if (is_3d_trans)
+		translations_z.push_back(offset_z);
 }
 
 
 
-void HealpixSampling::setOrientations(int _order, double _psi_step)
+void HealpixSampling::setOrientations(int _order, DOUBLE _psi_step)
 {
 
 	// Initialise
-	directions_angles.clear();
 	directions_ipix.clear();
+	rot_angles.clear();
+	tilt_angles.clear();
 	psi_angles.clear();
 
 	// Setup the HealPix object
@@ -284,27 +308,28 @@ void HealpixSampling::setOrientations(int _order, double _psi_step)
 	// 3D directions
 	if (is_3D)
 	{
-		double rot, tilt;
+		DOUBLE rot, tilt;
 		for (long int ipix = 0; ipix < healpix_base.Npix(); ipix++)
 		{
 			getDirectionFromHealPix(ipix, rot, tilt);
 
 			// Push back as Matrix1D's in the vectors
-			directions_angles.push_back(vectorR2(rot, tilt));
+			rot_angles.push_back(rot);
+			tilt_angles.push_back(tilt);
 			directions_ipix.push_back(ipix);
 
 
 		}
 //#define DEBUG_SAMPLING
 #ifdef  DEBUG_SAMPLING
-		writeAllOrientationsToBild("orients_all.bild", "1 0 0", 0.020);
+		writeAllOrientationsToBild("orients_all.bild", "1 0 0 ", 0.020);
 #endif
 		// Now remove symmetry-related pixels
 		// TODO check size of healpix_base.max_pixrad
 		removeSymmetryEquivalentPoints(0.5 * RAD2DEG(healpix_base.max_pixrad()));
 
 #ifdef  DEBUG_SAMPLING
-		writeAllOrientationsToBild("orients_sym.bild", "0 1 0", 0.021);
+		writeAllOrientationsToBild("orients_sym.bild", "0 1 0 ", 0.021);
 #endif
 
 		// Also remove limited tilt angles
@@ -312,14 +337,15 @@ void HealpixSampling::setOrientations(int _order, double _psi_step)
 
 		#ifdef  DEBUG_SAMPLING
 		if (ABS(limit_tilt) < 90.)
-			writeAllOrientationsToBild("orients_tilt.bild", "1 1 0", 0.022);
+			writeAllOrientationsToBild("orients_tilt.bild", "1 1 0 ", 0.022);
 #endif
 
 
 	}
 	else
 	{
-		directions_angles.push_back(vectorR2(0., 0.));
+		rot_angles.push_back(0.);
+		tilt_angles.push_back(0.);
 		directions_ipix.push_back(-1);
 	}
 
@@ -330,8 +356,8 @@ void HealpixSampling::setOrientations(int _order, double _psi_step)
 		psi_step = _psi_step;
 
 	int nr_psi = CEIL(360./psi_step);
-	double psi;
-	psi_step = 360./(double)nr_psi;
+	DOUBLE psi;
+	psi_step = 360./(DOUBLE)nr_psi;
 	for (int ipsi = 0; ipsi < nr_psi; ipsi++)
 	{
 		psi = ipsi * psi_step;
@@ -340,22 +366,27 @@ void HealpixSampling::setOrientations(int _order, double _psi_step)
 }
 
 /* Set only a single orientation */
-void HealpixSampling::setOneOrientation(double rot, double tilt, double psi)
+void HealpixSampling::addOneOrientation(DOUBLE rot, DOUBLE tilt, DOUBLE psi, bool do_clear)
 {
-	// Initialise
-	directions_angles.clear();
-	directions_ipix.clear();
-	psi_angles.clear();
+	if (do_clear)
+	{
+		directions_ipix.clear();
+		rot_angles.clear();
+		tilt_angles.clear();
+		psi_angles.clear();
+	}
 
 	// 3D directions
 	if (is_3D)
 	{
-		directions_angles.push_back(vectorR2(rot, tilt));
+		rot_angles.push_back(rot);
+		tilt_angles.push_back(tilt);
 		directions_ipix.push_back(-1);
 	}
 	else
 	{
-		directions_angles.push_back(vectorR2(0., 0.));
+		rot_angles.push_back(0.);
+		tilt_angles.push_back(0.);
 		directions_ipix.push_back(-1);
 	}
 
@@ -366,7 +397,7 @@ void HealpixSampling::setOneOrientation(double rot, double tilt, double psi)
 }
 
 
-void HealpixSampling::writeAllOrientationsToBild(FileName fn_bild, std::string rgb, double size)
+void HealpixSampling::writeAllOrientationsToBild(FileName fn_bild, std::string rgb, DOUBLE size)
 {
     std::ofstream out;
     out.open (fn_bild.c_str());
@@ -382,12 +413,12 @@ void HealpixSampling::writeAllOrientationsToBild(FileName fn_bild, std::string r
     out << ".arrow 0 0 0 0 0 1 0.01 \n";
 
 
-    Matrix1D<double> v(3);
+    Matrix1D<DOUBLE> v(3);
 	out << ".color " << rgb << std::endl;
 
-	for (unsigned long int ipix = 0; ipix < directions_angles.size(); ipix++)
+	for (unsigned long int ipix = 0; ipix < rot_angles.size(); ipix++)
 	{
-		Euler_angles2direction(XX(directions_angles[ipix]), YY(directions_angles[ipix]), v);
+		Euler_angles2direction(rot_angles[ipix], tilt_angles[ipix], v);
 		out <<  ".sphere " << XX(v) << " " << YY(v) << " " << ZZ(v) <<  floatToString(size) << std::endl;
 	}
 
@@ -395,7 +426,8 @@ void HealpixSampling::writeAllOrientationsToBild(FileName fn_bild, std::string r
 
 }
 
-void HealpixSampling::writeNonZeroPriorOrientationsToBild(FileName fn_bild, double rot_prior, double tilt_prior, std::string rgb, double size)
+void HealpixSampling::writeNonZeroPriorOrientationsToBild(FileName fn_bild, DOUBLE rot_prior, DOUBLE tilt_prior,
+		std::vector<int> &pointer_dir_nonzeroprior, std::string rgb, DOUBLE size)
 {
     std::ofstream out;
     out.open (fn_bild.c_str());
@@ -410,7 +442,7 @@ void HealpixSampling::writeNonZeroPriorOrientationsToBild(FileName fn_bild, doub
     out << ".color 0 0 1 \n";
     out << ".arrow 0 0 0 0 0 1 0.01 \n";
 
-    Matrix1D<double> v(3);
+    Matrix1D<DOUBLE> v(3);
 
 	Euler_angles2direction(rot_prior, tilt_prior, v);
 	out << ".color 0 0 0 \n";
@@ -420,7 +452,7 @@ void HealpixSampling::writeNonZeroPriorOrientationsToBild(FileName fn_bild, doub
 	for (unsigned long int ipix = 0; ipix < pointer_dir_nonzeroprior.size(); ipix++)
 	{
 		long int idir = pointer_dir_nonzeroprior[ipix];
-		Euler_angles2direction(XX(directions_angles[idir]), YY(directions_angles[idir]), v);
+		Euler_angles2direction(rot_angles[idir], tilt_angles[idir], v);
 		out <<  ".sphere " << XX(v) << " " << YY(v) << " " << ZZ(v) <<  floatToString(size) << std::endl;
 	}
 
@@ -429,9 +461,11 @@ void HealpixSampling::writeNonZeroPriorOrientationsToBild(FileName fn_bild, doub
 }
 
 void HealpixSampling::selectOrientationsWithNonZeroPriorProbability(
-		double prior_rot, double prior_tilt, double prior_psi,
-		double sigma_rot, double sigma_tilt, double sigma_psi,
-		double sigma_cutoff)
+		DOUBLE prior_rot, DOUBLE prior_tilt, DOUBLE prior_psi,
+		DOUBLE sigma_rot, DOUBLE sigma_tilt, DOUBLE sigma_psi,
+    	std::vector<int> &pointer_dir_nonzeroprior, std::vector<DOUBLE> &directions_prior,
+    	std::vector<int> &pointer_psi_nonzeroprior, std::vector<DOUBLE> &psi_prior,
+		DOUBLE sigma_cutoff)
 {
 	pointer_dir_nonzeroprior.clear();
 	directions_prior.clear();
@@ -440,31 +474,31 @@ void HealpixSampling::selectOrientationsWithNonZeroPriorProbability(
 	{
 
 		// Loop over all directions
-		double sumprior = 0.;
+		DOUBLE sumprior = 0.;
 		// Keep track of the closest distance to prevent 0 orientations
-		double best_ang = 9999.;
+		DOUBLE best_ang = 9999.;
 		long int best_idir = -999;
-		for (long int idir = 0; idir < directions_angles.size(); idir++)
+		for (long int idir = 0; idir < rot_angles.size(); idir++)
 		{
 
 			// Any prior involving rot and/or tilt.
 			if (sigma_rot > 0. || sigma_tilt > 0. )
 			{
 
-				Matrix1D<double> prior_direction, my_direction, sym_direction, best_direction;
+				Matrix1D<DOUBLE> prior_direction, my_direction, sym_direction, best_direction;
 				// Get the direction of the prior
 				Euler_angles2direction(prior_rot, prior_tilt, prior_direction);
 
 				// Get the current direction in the loop
-				Euler_angles2direction(XX(directions_angles[idir]), YY(directions_angles[idir]), my_direction);
+				Euler_angles2direction(rot_angles[idir], tilt_angles[idir], my_direction);
 
 				// Loop over all symmetry operators to find the operator that brings this direction nearest to the prior
-				double best_dotProduct = dotProduct(prior_direction, my_direction);
+				DOUBLE best_dotProduct = dotProduct(prior_direction, my_direction);
 				best_direction = my_direction;
 				for (int j = 0; j < R_repository.size(); j++)
 				{
 					sym_direction =  L_repository[j] * (my_direction.transpose() * R_repository[j]).transpose();
-					double my_dotProduct = dotProduct(prior_direction, sym_direction);
+					DOUBLE my_dotProduct = dotProduct(prior_direction, sym_direction);
 					if (my_dotProduct > best_dotProduct)
 					{
 						best_direction = sym_direction;
@@ -475,14 +509,14 @@ void HealpixSampling::selectOrientationsWithNonZeroPriorProbability(
 				if (sigma_rot > 0. && sigma_tilt > 0.)
 				{
 
-					double diffang = ACOSD( dotProduct(best_direction, prior_direction) );
+					DOUBLE diffang = ACOSD( dotProduct(best_direction, prior_direction) );
 					if (diffang > 180.) diffang = ABS(diffang - 360.);
 
 					// Only consider differences within sigma_cutoff * sigma_rot
 					if (diffang < sigma_cutoff * sigma_rot)
 					{
 						// TODO!!! If tilt is zero then any rot will be OK!!!!!
-						double prior = gaussian1D(diffang, sigma_rot, 0.);
+						DOUBLE prior = gaussian1D(diffang, sigma_rot, 0.);
 						pointer_dir_nonzeroprior.push_back(idir);
 						directions_prior.push_back(prior);
 						sumprior += prior;
@@ -497,16 +531,16 @@ void HealpixSampling::selectOrientationsWithNonZeroPriorProbability(
 				}
 				else if (sigma_rot > 0.)
 				{
-					double best_rot, best_tilt;
+					DOUBLE best_rot, best_tilt;
 
 					Euler_direction2angles(best_direction, best_rot, best_tilt);
-					double diffrot = ABS(best_rot - prior_rot);
+					DOUBLE diffrot = ABS(best_rot - prior_rot);
 					if (diffrot > 180.) diffrot = ABS(diffrot - 360.);
 
 					// Only consider differences within sigma_cutoff * sigma_rot
 					if (diffrot < sigma_cutoff * sigma_rot)
 					{
-						double prior = gaussian1D(diffrot, sigma_rot, 0.);
+						DOUBLE prior = gaussian1D(diffrot, sigma_rot, 0.);
 						pointer_dir_nonzeroprior.push_back(idir);
 						directions_prior.push_back(prior);
 						sumprior += prior;
@@ -523,16 +557,16 @@ void HealpixSampling::selectOrientationsWithNonZeroPriorProbability(
 				else if (sigma_tilt > 0.)
 				{
 
-					double best_rot, best_tilt;
+					DOUBLE best_rot, best_tilt;
 
 					Euler_direction2angles(best_direction, best_rot, best_tilt);
-					double difftilt = ABS(best_tilt - prior_tilt);
+					DOUBLE difftilt = ABS(best_tilt - prior_tilt);
 					if (difftilt > 180.) difftilt = ABS(difftilt - 360.);
 
 					// Only consider differences within sigma_cutoff * sigma_tilt
 					if (difftilt < sigma_cutoff * sigma_tilt)
 					{
-						double prior = gaussian1D(difftilt, sigma_tilt, 0.);
+						DOUBLE prior = gaussian1D(difftilt, sigma_tilt, 0.);
 						pointer_dir_nonzeroprior.push_back(idir);
 						directions_prior.push_back(prior);
 						sumprior += prior;
@@ -573,7 +607,7 @@ void HealpixSampling::selectOrientationsWithNonZeroPriorProbability(
 		}
 
 #ifdef  DEBUG_SAMPLING
-		writeNonZeroPriorOrientationsToBild("orients_local.bild", prior_rot, prior_tilt, "0 0 1", 0.023);
+		writeNonZeroPriorOrientationsToBild("orients_local.bild", prior_rot, prior_tilt, pointer_dir_nonzeroprior, "0 0 1", 0.023);
 		std::cerr << " directions_prior.size()= " << directions_prior.size() << " pointer_dir_nonzeroprior.size()= " << pointer_dir_nonzeroprior.size() << std::endl;
 		std::cerr << " sumprior= " << sumprior << std::endl;
 		char c;
@@ -594,20 +628,20 @@ void HealpixSampling::selectOrientationsWithNonZeroPriorProbability(
 	pointer_psi_nonzeroprior.clear();
 	psi_prior.clear();
 
-	double sumprior = 0.;
-	double best_diff = 9999.;
+	DOUBLE sumprior = 0.;
+	DOUBLE best_diff = 9999.;
 	long int best_ipsi = -999;
 	for (long int ipsi = 0; ipsi < psi_angles.size(); ipsi++)
 	{
 		if (sigma_psi > 0.)
 		{
-			double diffpsi = ABS(psi_angles[ipsi] - prior_psi);
+			DOUBLE diffpsi = ABS(psi_angles[ipsi] - prior_psi);
 			if (diffpsi > 180.) diffpsi = ABS(diffpsi - 360.);
 
 			// Only consider differences within sigma_cutoff * sigma_psi
 			if (diffpsi < sigma_cutoff * sigma_psi)
 			{
-				double prior = gaussian1D(diffpsi, sigma_psi, 0.);
+				DOUBLE prior = gaussian1D(diffpsi, sigma_psi, 0.);
 				pointer_psi_nonzeroprior.push_back(ipsi);
 				psi_prior.push_back(prior);
 				sumprior += prior;
@@ -657,79 +691,6 @@ void HealpixSampling::selectOrientationsWithNonZeroPriorProbability(
 
 }
 
-void HealpixSampling::randomSelectionNonZeroPriorProbability(double fraction_to_keep)
-{
-
-    // If we had not yet determined the prior probability vectors, just fill them with an even distribution
-    if (is_3D)
-    {
-		if (directions_prior.size() == 0)
-		{
-			for (long int idir = 0; idir < directions_angles.size(); idir++)
-			{
-				pointer_dir_nonzeroprior.push_back(idir);
-				directions_prior.push_back(1.);
-			} // this will be re-normalised below!
-		}
-    }
-    if (psi_prior.size() == 0)
-    {
-    	for (long int ipsi = 0; ipsi < psi_angles.size(); ipsi++)
-		{
-			pointer_psi_nonzeroprior.push_back(ipsi);
-			psi_prior.push_back(1.);
-		} // this will be re-normalised below!
-    }
-
-
-
-	// Act on directions
-    double sum_prior;
-    if (is_3D)
-    {
-		std::vector<double> copy_directions_prior;
-		std::vector<int> copy_pointer_dir_nonzeroprior;
-		sum_prior = 0.;
-		for (long int i = 0; i < directions_prior.size(); i++)
-		{
-			double aux = rnd_unif();
-			if (aux < fraction_to_keep)
-			{
-				copy_directions_prior.push_back(directions_prior[i]);
-				copy_pointer_dir_nonzeroprior.push_back(pointer_dir_nonzeroprior[i]);
-				sum_prior += directions_prior[i];
-			}
-		}
-		directions_prior = copy_directions_prior;
-		pointer_dir_nonzeroprior = copy_pointer_dir_nonzeroprior;
-		// renormalise
-		for (long int idir = 0; idir < directions_prior.size(); idir++)
-			directions_prior[idir] /= sum_prior;
-    }
-
-    // And act on psi-angles
-    std::vector<double> copy_psi_prior;
-    std::vector<int> copy_pointer_psi_nonzeroprior;
-    sum_prior = 0.;
-    for (long int i = 0; i < psi_prior.size(); i++)
-    {
-		double aux = rnd_unif();
-		if (aux < fraction_to_keep)
-    	{
-    		copy_psi_prior.push_back(psi_prior[i]);
-    		copy_pointer_psi_nonzeroprior.push_back(pointer_psi_nonzeroprior[i]);
-    		sum_prior += psi_prior[i];
-    	}
-    }
-    psi_prior = copy_psi_prior;
-    pointer_psi_nonzeroprior = copy_pointer_psi_nonzeroprior;
-    // renormalise
-    for (long int ipsi = 0; ipsi < psi_prior.size(); ipsi++)
-		psi_prior[ipsi] /= sum_prior;
-
-}
-
-
 FileName HealpixSampling::symmetryGroup()
 {
 	return fn_sym;
@@ -747,7 +708,7 @@ long int HealpixSampling::getHealPixIndex(long int idir)
 	return directions_ipix[idir];
 }
 
-void HealpixSampling::checkDirection(double &rot, double &tilt)
+void HealpixSampling::checkDirection(DOUBLE &rot, DOUBLE &tilt)
 {
 
 	// The geometrical considerations about the symmetry below require that rot = [-180,180] and tilt [0,180]
@@ -772,24 +733,25 @@ void HealpixSampling::checkDirection(double &rot, double &tilt)
 
 }
 
-void HealpixSampling::getDirectionFromHealPix(long int ipix, double &rot, double &tilt)
+void HealpixSampling::getDirectionFromHealPix(long int ipix, DOUBLE &rot, DOUBLE &tilt)
 {
-	double zz, phi;
-	healpix_base.pix2ang_z_phi(ipix, zz, phi);
-	rot = RAD2DEG(phi);
-	tilt = ACOSD(zz);
-
-	// The geometrical considerations about the symmetry below require that rot = [-180,180] and tilt [0,180]
-	checkDirection(rot, tilt);
+    // this one always has to be double (also for SINGLE_PRECISION CALCULATIONS) for call to external library 
+    double zz, phi;
+    healpix_base.pix2ang_z_phi(ipix, zz, phi);
+    rot = RAD2DEG(phi);
+    tilt = ACOSD(zz);
+    
+    // The geometrical considerations about the symmetry below require that rot = [-180,180] and tilt [0,180]
+    checkDirection(rot, tilt);
 
 }
 
-double HealpixSampling::getTranslationalSampling(int adaptive_oversampling)
+DOUBLE HealpixSampling::getTranslationalSampling(int adaptive_oversampling)
 {
 	return offset_step / std::pow(2., adaptive_oversampling);
 }
 
-double HealpixSampling::getAngularSampling(int adaptive_oversampling)
+DOUBLE HealpixSampling::getAngularSampling(int adaptive_oversampling)
 {
 	if (is_3D)
 	{
@@ -800,19 +762,21 @@ double HealpixSampling::getAngularSampling(int adaptive_oversampling)
 		return psi_step / std::pow(2., adaptive_oversampling);
 }
 
-long int HealpixSampling::NrDirections(int oversampling_order, bool include_zeroprior)
+long int HealpixSampling::NrDirections(int oversampling_order, const std::vector<int> *pointer_dir_nonzeroprior)
 {
-	long int mysize = (orientational_prior_mode == NOPRIOR || include_zeroprior) ? directions_angles.size() : pointer_dir_nonzeroprior.size();
+	long int mysize = (pointer_dir_nonzeroprior != NULL && (*pointer_dir_nonzeroprior).size() > 0) ?
+			(*pointer_dir_nonzeroprior).size() : rot_angles.size();
 	if (oversampling_order == 0)
 		return mysize;
 	else
 		return ROUND(std::pow(2., oversampling_order * 2)) * mysize;
 }
 
-long int HealpixSampling::NrPsiSamplings(int oversampling_order, bool include_zeroprior)
+long int HealpixSampling::NrPsiSamplings(int oversampling_order, const std::vector<int> *pointer_psi_nonzeroprior)
 {
 
-	long int mysize = (orientational_prior_mode == NOPRIOR || include_zeroprior) ? psi_angles.size() : pointer_psi_nonzeroprior.size();
+	long int mysize = (pointer_psi_nonzeroprior != NULL && (*pointer_psi_nonzeroprior).size() > 0) ?
+			(*pointer_psi_nonzeroprior).size() : psi_angles.size();
 	if (oversampling_order == 0)
 		return mysize;
 	else
@@ -822,20 +786,22 @@ long int HealpixSampling::NrPsiSamplings(int oversampling_order, bool include_ze
 long int HealpixSampling::NrTranslationalSamplings(int oversampling_order)
 {
 	if (oversampling_order == 0)
-		return translations.size();
+		return translations_x.size();
 	else
 	{
 		if (is_3d_trans)
-			return ROUND(std::pow(2., oversampling_order * 3)) * translations.size();
+			return ROUND(std::pow(2., oversampling_order * 3)) * translations_x.size();
 		else
-			return ROUND(std::pow(2., oversampling_order * 2)) * translations.size();
+			return ROUND(std::pow(2., oversampling_order * 2)) * translations_x.size();
 	}
 }
 
-long int HealpixSampling::NrSamplingPoints(int oversampling_order, bool include_zeroprior)
+long int HealpixSampling::NrSamplingPoints(int oversampling_order,
+		const std::vector<int> *pointer_dir_nonzeroprior,
+		const std::vector<int> *pointer_psi_nonzeroprior)
 {
-	return NrDirections(oversampling_order, include_zeroprior) *
-		   NrPsiSamplings(oversampling_order, include_zeroprior) *
+	return NrDirections(oversampling_order, pointer_dir_nonzeroprior) *
+		   NrPsiSamplings(oversampling_order, pointer_psi_nonzeroprior) *
 		   NrTranslationalSamplings(oversampling_order);
 }
 
@@ -858,21 +824,21 @@ int HealpixSampling::oversamplingFactorTranslations(int oversampling_order)
 }
 
 
-void HealpixSampling::getDirection(long int idir, double &rot, double &tilt)
+void HealpixSampling::getDirection(long int idir, DOUBLE &rot, DOUBLE &tilt)
 {
 #ifdef DEBUG_CHECKSIZES
-	if (idir >= directions_angles.size())
+	if (idir >= rot_angles.size())
 	{
-		std::cerr<< "idir= "<<idir<<" directions_angles.size()= "<< directions_angles.size() <<std::endl;
-		REPORT_ERROR("idir >= directions_angles.size()");
+		std::cerr<< "idir= "<<idir<<" rot_angles.size()= "<< rot_angles.size() <<std::endl;
+		REPORT_ERROR("idir >= rot_angles.size()");
 	}
 #endif
 
-	rot  = XX(directions_angles[idir]);
-	tilt = YY(directions_angles[idir]);
+	rot  = rot_angles[idir];
+	tilt = tilt_angles[idir];
 }
 
-void HealpixSampling::getPsiAngle(long int ipsi, double &psi)
+void HealpixSampling::getPsiAngle(long int ipsi, DOUBLE &psi)
 {
 #ifdef DEBUG_CHECKSIZES
 	if (ipsi >= psi_angles.size())
@@ -884,23 +850,26 @@ void HealpixSampling::getPsiAngle(long int ipsi, double &psi)
 	psi = psi_angles[ipsi];
 }
 
-void HealpixSampling::getTranslation(long int itrans, Matrix1D<double> &trans)
+void HealpixSampling::getTranslation(long int itrans, DOUBLE &trans_x, DOUBLE &trans_y, DOUBLE &trans_z)
 {
 #ifdef DEBUG_CHECKSIZES
-if (itrans >= translations.size())
+if (itrans >= translations_x.size())
 {
-	std::cerr<< "itrans= "<<itrans<<" translations.size()= "<< translations.size() <<std::endl;
-	REPORT_ERROR("itrans >= translations.size()");
+	std::cerr<< "itrans= "<<itrans<<" translations_x.size()= "<< translations_x.size() <<std::endl;
+	REPORT_ERROR("itrans >= translations_x.size()");
 }
 #endif
-	trans = translations[itrans];
+	trans_x = translations_x[itrans];
+	trans_y = translations_y[itrans];
+	if (is_3d_trans)
+		trans_z = translations_z[itrans];
 }
 
 long int HealpixSampling::getPositionSamplingPoint(int iclass, long int idir, long int ipsi, long int itrans)
 {
-	return iclass * directions_angles.size() * psi_angles.size() * translations.size()
-		+ idir * psi_angles.size() * translations.size()
-		+ ipsi * translations.size() + itrans;
+	return iclass * rot_angles.size() * psi_angles.size() * translations_x.size()
+		+ idir * psi_angles.size() * translations_x.size()
+		+ ipsi * translations_x.size() + itrans;
 }
 
 long int HealpixSampling::getPositionOversampledSamplingPoint(long int ipos, int oversampling_order, int iover_rot, int iover_trans)
@@ -917,42 +886,59 @@ long int HealpixSampling::getPositionOversampledSamplingPoint(long int ipos, int
 }
 
 void HealpixSampling::getTranslations(long int itrans, int oversampling_order,
-		std::vector<Matrix1D<double> > &my_translations)
+		std::vector<DOUBLE > &my_translations_x,
+		std::vector<DOUBLE > &my_translations_y,
+		std::vector<DOUBLE > &my_translations_z)
 {
 
 #ifdef DEBUG_CHECKSIZES
-	if (itrans >= translations.size())
+	if (itrans >= translations_x.size())
 	{
-		std::cerr<< "itrans= "<<itrans<<" translations.size()= "<< translations.size() <<std::endl;
-		REPORT_ERROR("itrans >= translations.size()");
+		std::cerr<< "itrans= "<<itrans<<" translations_x.size()= "<< translations_x.size() <<std::endl;
+		REPORT_ERROR("itrans >= translations_x.size()");
 	}
 #endif
-	my_translations.clear();
+	my_translations_x.clear();
+	my_translations_y.clear();
+	my_translations_z.clear();
 	if (oversampling_order == 0)
 	{
-		my_translations.push_back(translations[itrans]);
+		my_translations_x.push_back(translations_x[itrans]);
+		my_translations_y.push_back(translations_y[itrans]);
+		if (is_3d_trans)
+			my_translations_z.push_back(translations_z[itrans]);
 	}
 	else
 	{
 		int nr_oversamples = ROUND(std::pow(2., oversampling_order));
-
-		for (int itrans_overy = 0; itrans_overy < nr_oversamples; itrans_overy++)
+		if (is_3d_trans)
 		{
-			double over_yoff = YY(translations[itrans]) - 0.5 * offset_step + (0.5 + itrans_overy) * offset_step / nr_oversamples;
-			for (int itrans_overx = 0; itrans_overx < nr_oversamples; itrans_overx++)
+			for (int itrans_overy = 0; itrans_overy < nr_oversamples; itrans_overy++)
 			{
-				double over_xoff = XX(translations[itrans]) - 0.5 * offset_step + (0.5 + itrans_overx) * offset_step / nr_oversamples;
-				if (is_3d_trans)
+				DOUBLE over_yoff = translations_y[itrans] - 0.5 * offset_step + (0.5 + itrans_overy) * offset_step / nr_oversamples;
+				for (int itrans_overx = 0; itrans_overx < nr_oversamples; itrans_overx++)
 				{
+					DOUBLE over_xoff = translations_x[itrans] - 0.5 * offset_step + (0.5 + itrans_overx) * offset_step / nr_oversamples;
 					for (int itrans_overz = 0; itrans_overz < nr_oversamples; itrans_overz++)
 					{
-						double over_zoff = ZZ(translations[itrans]) - 0.5 * offset_step + (0.5 + itrans_overz) * offset_step / nr_oversamples;
-						my_translations.push_back(vectorR3(over_xoff, over_yoff, over_zoff));
+						DOUBLE over_zoff = translations_z[itrans] - 0.5 * offset_step + (0.5 + itrans_overz) * offset_step / nr_oversamples;
+						my_translations_x.push_back(over_xoff);
+						my_translations_y.push_back(over_yoff);
+						my_translations_z.push_back(over_zoff);
 					}
 				}
-				else
+			}
+		}
+		else
+		{
+			for (int itrans_overy = 0; itrans_overy < nr_oversamples; itrans_overy++)
+			{
+				DOUBLE over_yoff = translations_y[itrans] - 0.5 * offset_step + (0.5 + itrans_overy) * offset_step / nr_oversamples;
+				for (int itrans_overx = 0; itrans_overx < nr_oversamples; itrans_overx++)
 				{
-					my_translations.push_back(vectorR2(over_xoff, over_yoff));
+					DOUBLE over_xoff = translations_x[itrans] - 0.5 * offset_step + (0.5 + itrans_overx) * offset_step / nr_oversamples;
+					my_translations_x.push_back(over_xoff);
+					my_translations_y.push_back(over_yoff);
 				}
 			}
 		}
@@ -960,22 +946,26 @@ void HealpixSampling::getTranslations(long int itrans, int oversampling_order,
 
 	if (ABS(random_perturbation) > 0.)
 	{
-		double myperturb = random_perturbation * offset_step;
-		for (int iover = 0; iover < my_translations.size(); iover++)
+		DOUBLE myperturb = random_perturbation * offset_step;
+		for (int iover = 0; iover < my_translations_x.size(); iover++)
 		{
-			XX(my_translations[iover]) += myperturb;
-			YY(my_translations[iover]) += myperturb;
+			my_translations_x[iover] += myperturb;
+			my_translations_y[iover] += myperturb;
 			if (is_3d_trans)
-				ZZ(my_translations[iover]) += myperturb;
+				my_translations_z[iover] += myperturb;
 		}
 	}
 
 }
 
 void HealpixSampling::getOrientations(long int idir, long int ipsi, int oversampling_order,
-		std::vector<Matrix1D<double> > &my_orientations)
+		std::vector<DOUBLE > &my_rot, std::vector<DOUBLE > &my_tilt, std::vector<DOUBLE > &my_psi,
+		std::vector<int> &pointer_dir_nonzeroprior, std::vector<DOUBLE> &directions_prior,
+		std::vector<int> &pointer_psi_nonzeroprior, std::vector<DOUBLE> &psi_prior)
 {
-	my_orientations.clear();
+	my_rot.clear();
+	my_tilt.clear();
+	my_psi.clear();
 	long int my_idir, my_ipsi;
 	if (orientational_prior_mode == NOPRIOR)
 	{
@@ -1001,10 +991,10 @@ void HealpixSampling::getOrientations(long int idir, long int ipsi, int oversamp
 	}
 
 #ifdef DEBUG_CHECKSIZES
-		if (my_idir >= directions_angles.size())
+		if (my_idir >= rot_angles.size())
 		{
-			std::cerr<< "my_idir= "<<my_idir<<" directions_angles.size()= "<< directions_angles.size() <<std::endl;
-			REPORT_ERROR("my_idir >= directions_angles.size()");
+			std::cerr<< "my_idir= "<<my_idir<<" rot_angles.size()= "<< rot_angles.size() <<std::endl;
+			REPORT_ERROR("my_idir >= rot_angles.size()");
 		}
 		if (my_ipsi >= psi_angles.size())
 		{
@@ -1015,14 +1005,14 @@ void HealpixSampling::getOrientations(long int idir, long int ipsi, int oversamp
 
 	if (oversampling_order == 0)
 	{
-		my_orientations.push_back(vectorR3(XX(directions_angles[my_idir]),
-										   YY(directions_angles[my_idir]),
-										   psi_angles[my_ipsi]));
+		my_rot.push_back(rot_angles[my_idir]);
+		my_tilt.push_back(tilt_angles[my_idir]);
+		my_psi.push_back(psi_angles[my_ipsi]);
 	}
 	else if (!is_3D)
 	{
 		// for 2D sampling, only push back oversampled psi rotations
-		pushbackOversampledPsiAngles(my_ipsi, oversampling_order, 0., 0., my_orientations);
+		pushbackOversampledPsiAngles(my_ipsi, oversampling_order, 0., 0., my_rot, my_tilt, my_psi);
 	}
 	else
 	{
@@ -1030,7 +1020,7 @@ void HealpixSampling::getOrientations(long int idir, long int ipsi, int oversamp
 		Healpix_Base HealPixOver(oversampling_order + healpix_order, NEST);
 		int fact = HealPixOver.Nside()/healpix_base.Nside();
 		int x, y, face;
-		double rot, tilt;
+		DOUBLE rot, tilt;
 		// Get x, y and face for the original, coarse grid
 		long int ipix = directions_ipix[my_idir];
 		healpix_base.nest2xyf(ipix, x, y, face);
@@ -1040,6 +1030,7 @@ void HealpixSampling::getOrientations(long int idir, long int ipsi, int oversamp
 			for (int i = fact * x; i < fact * (x+1); ++i)
 			{
 				long int overpix = HealPixOver.xyf2nest(i, j, face);
+                                // this one always has to be double (also for SINGLE_PRECISION CALCULATIONS) for call to external library 
 				double zz, phi;
 				HealPixOver.pix2ang_z_phi(overpix, zz, phi);
 				rot = RAD2DEG(phi);
@@ -1048,7 +1039,7 @@ void HealpixSampling::getOrientations(long int idir, long int ipsi, int oversamp
 				// The geometrical considerations about the symmetry below require that rot = [-180,180] and tilt [0,180]
 				checkDirection(rot, tilt);
 
-				pushbackOversampledPsiAngles(my_ipsi, oversampling_order, rot, tilt, my_orientations);
+				pushbackOversampledPsiAngles(my_ipsi, oversampling_order, rot, tilt, my_rot, my_tilt, my_psi);
 			}
 		}
 	}
@@ -1057,26 +1048,26 @@ void HealpixSampling::getOrientations(long int idir, long int ipsi, int oversamp
 	// Random perturbation
 	if (ABS(random_perturbation) > 0.)
 	{
-		double myperturb = random_perturbation * getAngularSampling();
-		for (int iover = 0; iover < my_orientations.size(); iover++)
+		DOUBLE myperturb = random_perturbation * getAngularSampling();
+		for (int iover = 0; iover < my_rot.size(); iover++)
 		{
 			if (is_3D)
 			{
-				Matrix2D<double> A, R;
-				Euler_angles2matrix(XX(my_orientations[iover]),
-									YY(my_orientations[iover]),
-									ZZ(my_orientations[iover]),
+				Matrix2D<DOUBLE> A, R;
+				Euler_angles2matrix(my_rot[iover],
+									my_tilt[iover],
+									my_psi[iover],
 									A);
 				Euler_angles2matrix(myperturb, myperturb, myperturb, R);
 				A = A * R;
 				Euler_matrix2angles(A,
-									XX(my_orientations[iover]),
-									YY(my_orientations[iover]),
-									ZZ(my_orientations[iover]));
+									my_rot[iover],
+									my_tilt[iover],
+									my_psi[iover]);
 			}
 			else
 			{
-				ZZ(my_orientations[iover]) += myperturb;
+				my_psi[iover] += myperturb;
 			}
 		}
 	}
@@ -1085,119 +1076,90 @@ void HealpixSampling::getOrientations(long int idir, long int ipsi, int oversamp
 
 }
 
-double HealpixSampling::getPriorProbability(long int idir, long int ipsi)
-{
-
-#ifdef DEBUG_CHECKSIZES
-	if (idir >= directions_prior.size())
-	{
-		std::cerr<< "idir= "<<idir<<" directions_prior.size()= "<< directions_prior.size() <<std::endl;
-		REPORT_ERROR("idir >= directions_prior.size()");
-	}
-	if (ipsi >= psi_prior.size())
-	{
-		std::cerr<< "ipsi= "<<ipsi<<" psi_prior.size()= "<< psi_prior.size() <<std::endl;
-		REPORT_ERROR("ipsi >= psi_prior.size()");
-	}
-#endif
-	return directions_prior[idir] * psi_prior[ipsi];
-}
-
-
-long int HealpixSampling::getDirectionNumberAlsoZeroPrior(long int idir)
-{
-#ifdef DEBUG_CHECKSIZES
-	if (idir >= pointer_dir_nonzeroprior.size())
-	{
-		std::cerr<< "idir= "<<idir<<" pointer_dir_nonzeroprior.size()= "<< pointer_dir_nonzeroprior.size() <<std::endl;
-		REPORT_ERROR("idir >= pointer_dir_nonzeroprior.size()");
-	}
-#endif
-	return pointer_dir_nonzeroprior[idir];
-}
-
-long int HealpixSampling::getPsiNumberAlsoZeroPrior(long int ipsi)
-{
-#ifdef DEBUG_CHECKSIZES
-	if (ipsi >= pointer_psi_nonzeroprior.size())
-	{
-		std::cerr<< "ipsi= "<<ipsi<<" pointer_psi_nonzeroprior.size()= "<< pointer_psi_nonzeroprior.size() <<std::endl;
-		REPORT_ERROR("ipsi >= pointer_psi_nonzeroprior.size()");
-	}
-#endif
-
-	return pointer_psi_nonzeroprior[ipsi];
-}
 
 void HealpixSampling::pushbackOversampledPsiAngles(long int ipsi, int oversampling_order,
-		double rot, double tilt, std::vector<Matrix1D<double> > &oversampled_orientations)
+		DOUBLE rot, DOUBLE tilt, std::vector<DOUBLE> &oversampled_rot,
+		std::vector<DOUBLE> &oversampled_tilt, std::vector<DOUBLE> &oversampled_psi)
 {
 
 	if (oversampling_order == 0)
 	{
-		oversampled_orientations.push_back(vectorR3(rot, tilt, psi_angles[ipsi]));
+		oversampled_rot.push_back(rot);
+		oversampled_tilt.push_back(tilt);
+		oversampled_psi.push_back(psi_angles[ipsi]);
 	}
 	else
 	{
 		int nr_ipsi_over = ROUND(std::pow(2., oversampling_order));
 		for (int ipsi_over = 0; ipsi_over < nr_ipsi_over; ipsi_over++)
 		{
-			double overpsi = psi_angles[ipsi] - 0.5 * psi_step + (0.5 + ipsi_over) * psi_step / nr_ipsi_over;
-			oversampled_orientations.push_back(vectorR3(rot, tilt, overpsi));
+			DOUBLE overpsi = psi_angles[ipsi] - 0.5 * psi_step + (0.5 + ipsi_over) * psi_step / nr_ipsi_over;
+			oversampled_rot.push_back(rot);
+			oversampled_tilt.push_back(tilt);
+			oversampled_psi.push_back(overpsi);
 		}
 	}
 
 }
 
 /* Calculate an angular distance between two sets of Euler angles */
-double HealpixSampling::calculateAngularDistance(double rot1, double tilt1, double psi1,
-		double rot2, double tilt2, double psi2)
+DOUBLE HealpixSampling::calculateAngularDistance(DOUBLE rot1, DOUBLE tilt1, DOUBLE psi1,
+		DOUBLE rot2, DOUBLE tilt2, DOUBLE psi2)
 {
-	Matrix1D<double>  direction1(3), direction1p(3), direction2(3);
-	Euler_angles2direction(rot1, tilt1, direction1);
-	Euler_angles2direction(rot2, tilt2, direction2);
-
-	// Find the symmetry operation where the Distance based on Euler axes is minimal
-	double min_axes_dist = 3600.;
-	double rot2p, tilt2p, psi2p;
-	Matrix2D<double> E1, E2;
-	Matrix1D<double> v1, v2;
-	for (int j = 0; j < R_repository.size(); j++)
+
+	if (is_3D)
 	{
+		Matrix1D<DOUBLE>  direction1(3), direction1p(3), direction2(3);
+		Euler_angles2direction(rot1, tilt1, direction1);
+		Euler_angles2direction(rot2, tilt2, direction2);
+
+		// Find the symmetry operation where the Distance based on Euler axes is minimal
+		DOUBLE min_axes_dist = 3600.;
+		DOUBLE rot2p, tilt2p, psi2p;
+		Matrix2D<DOUBLE> E1, E2;
+		Matrix1D<DOUBLE> v1, v2;
+		for (int j = 0; j < R_repository.size(); j++)
+		{
 
-        Euler_apply_transf(L_repository[j], R_repository[j], rot2, tilt2, psi2, rot2p, tilt2p, psi2p);
+			Euler_apply_transf(L_repository[j], R_repository[j], rot2, tilt2, psi2, rot2p, tilt2p, psi2p);
 
-	    // Distance based on Euler axes
-	    Euler_angles2matrix(rot1, tilt1, psi1, E1);
-	    Euler_angles2matrix(rot2p, tilt2p, psi2p, E2);
-	    double axes_dist = 0;
-	    for (int i = 0; i < 3; i++)
-	    {
-	        E1.getRow(i, v1);
-	        E2.getRow(i, v2);
-	        axes_dist += ACOSD(CLIP(dotProduct(v1, v2), -1., 1.));
-	    }
-	    axes_dist /= 3.;
+			// Distance based on Euler axes
+			Euler_angles2matrix(rot1, tilt1, psi1, E1);
+			Euler_angles2matrix(rot2p, tilt2p, psi2p, E2);
+			DOUBLE axes_dist = 0;
+			for (int i = 0; i < 3; i++)
+			{
+				E1.getRow(i, v1);
+				E2.getRow(i, v2);
+				axes_dist += ACOSD(CLIP(dotProduct(v1, v2), -1., 1.));
+			}
+			axes_dist /= 3.;
 
-	    if (axes_dist < min_axes_dist)
-	    	min_axes_dist = axes_dist;
+			if (axes_dist < min_axes_dist)
+				min_axes_dist = axes_dist;
 
-	}// for all symmetry operations j
+		}// for all symmetry operations j
 
-	return min_axes_dist;
+		return min_axes_dist;
+	}
+	else
+	{
+		DOUBLE diff = ABS(psi2 - psi1);
+		return realWRAP(diff, 0., 360.);
+	}
 }
 
-void HealpixSampling::writeBildFileOrientationalDistribution(MultidimArray<double> &pdf_direction,
-		FileName &fn_bild, double R, double offset, double Rmax_frac, double width_frac)
+void HealpixSampling::writeBildFileOrientationalDistribution(MultidimArray<DOUBLE> &pdf_direction,
+		FileName &fn_bild, DOUBLE R, DOUBLE offset, DOUBLE Rmax_frac, DOUBLE width_frac)
 {
 	if (!is_3D)
 		return;
 
-	if (XSIZE(pdf_direction) != directions_angles.size())
-		REPORT_ERROR("HealpixSampling::writeBildFileOrientationalDistribution XSIZE(pdf_direction) != directions_angles.size()!");
+	if (XSIZE(pdf_direction) != rot_angles.size())
+		REPORT_ERROR("HealpixSampling::writeBildFileOrientationalDistribution XSIZE(pdf_direction) != rot_angles.size()!");
 
 
-	double pdfmax, pdfmin, pdfmean, pdfsigma;
+	DOUBLE pdfmax, pdfmin, pdfmean, pdfsigma;
 	pdf_direction.computeStats(pdfmean, pdfsigma, pdfmin, pdfmax);
 
 	std::ofstream fh_bild;
@@ -1206,27 +1168,27 @@ void HealpixSampling::writeBildFileOrientationalDistribution(MultidimArray<doubl
     	REPORT_ERROR("HealpixSampling::writeBildFileOrientationalDistribution: cannot open " + fn_bild);
 
     // 2 * PI * R = 360 degrees, 2*radius should cover angular sampling at width_frac=1
-    double width = width_frac * PI*R*(getAngularSampling()/360.);
-    Matrix1D<double> v(3);
+    DOUBLE width = width_frac * PI*R*(getAngularSampling()/360.);
+    Matrix1D<DOUBLE> v(3);
 
-    for (long int iang = 0; iang < directions_angles.size(); iang++)
+    for (long int iang = 0; iang < rot_angles.size(); iang++)
     {
-     	double pdf = DIRECT_A1D_ELEM(pdf_direction, iang);
+     	DOUBLE pdf = DIRECT_A1D_ELEM(pdf_direction, iang);
 
      	// Don't make a cylinder for pdf==0
      	if (pdf > 0.)
      	{
 			// Colour from blue to red according to deviations from sigma_pdf
-			double colscale = (pdf - pdfmean) / pdfsigma;
+			DOUBLE colscale = (pdf - pdfmean) / pdfsigma;
 			colscale = XMIPP_MIN(colscale, 5.);
 			colscale = XMIPP_MAX(colscale, -1.);
 			colscale /= 6.;
 			colscale += 1./6.; // colscale ranges from 0 (-5 sigma) to 1 (+5 sigma)
 
 			// The length of the cylinder will depend on the pdf_direction
-			double Rp = R + Rmax_frac * R * pdf / pdfmax;
+			DOUBLE Rp = R + Rmax_frac * R * pdf / pdfmax;
 
-			Euler_angles2direction(XX(directions_angles[iang]), YY(directions_angles[iang]), v);
+			Euler_angles2direction(rot_angles[iang], tilt_angles[iang], v);
 
 			// Don't include cylinders with zero length, as chimera will complain about that....
 			if (ABS((R - Rp) * XX(v)) > 0.01 ||
@@ -1262,25 +1224,29 @@ void HealpixSampling::removePointsOutsideLimitedTiltAngles()
 
     if (ABS(limit_tilt) < 90.)
     {
-    	std::vector<Matrix1D<double> > pruned_directions_angles;
+    	std::vector<DOUBLE> pruned_rot_angles;
+    	std::vector<DOUBLE> pruned_tilt_angles;
 		std::vector<int>               pruned_directions_ipix;
-		pruned_directions_angles.clear();
+		pruned_rot_angles.clear();
+		pruned_tilt_angles.clear();
 		pruned_directions_ipix.clear();
 
-		for (long int i = 0; i < directions_angles.size(); i++)
+		for (long int i = 0; i < tilt_angles.size(); i++)
 		{
-			double tilt = YY(directions_angles[i]);
+			DOUBLE tilt = tilt_angles[i];
 			// Let tilt angle range from -90 to 90.
 			if (tilt > 90.) tilt -= 180.;
 
 			// Keep side views || keep top views
 			if ( (limit_tilt > 0. && ABS(tilt) >= ABS(limit_tilt)) || (limit_tilt < 0. && ABS(tilt) <= ABS(limit_tilt)) )
 			{
-				pruned_directions_angles.push_back(directions_angles[i]);
+				pruned_rot_angles.push_back(rot_angles[i]);
+				pruned_tilt_angles.push_back(tilt_angles[i]);
 				pruned_directions_ipix.push_back(directions_ipix[i]);
 			}
 		}
-		directions_angles = pruned_directions_angles;
+		rot_angles = pruned_rot_angles;
+		tilt_angles = pruned_tilt_angles;
 		directions_ipix   = pruned_directions_ipix;
     }
 
@@ -1314,18 +1280,18 @@ void HealpixSampling::removePointsOutsideLimitedTiltAngles()
  *  e-mail address 'xmipp at cnb.csic.es'
  ***************************************************************************/
 
-void HealpixSampling::removeSymmetryEquivalentPoints(double max_ang)
+void HealpixSampling::removeSymmetryEquivalentPoints(DOUBLE max_ang)
 {
     // Maximum distance
-    double cos_max_ang = cos(DEG2RAD(max_ang));
-    double my_dotProduct;
-    Matrix1D<double>  direction(3), direction1(3);
-    std::vector<Matrix1D<double> > directions_vector;
+    DOUBLE cos_max_ang = cos(DEG2RAD(max_ang));
+    DOUBLE my_dotProduct;
+    Matrix1D<DOUBLE>  direction(3), direction1(3);
+    std::vector<Matrix1D<DOUBLE> > directions_vector;
 
     // Calculate all vectors and fill directions_vector
-    for (long int i = 0; i < directions_angles.size(); i++)
+    for (long int i = 0; i < rot_angles.size(); i++)
     {
-    	Euler_angles2direction(XX(directions_angles[i]), YY(directions_angles[i]), direction);
+    	Euler_angles2direction(rot_angles[i], tilt_angles[i], direction);
     	directions_vector.push_back(direction);
     }
 
@@ -1340,21 +1306,18 @@ void HealpixSampling::removeSymmetryEquivalentPoints(double max_ang)
     // For large numbers, the sampling is very fine and the probability distributions are probably delta functions anyway
     // Large numbers take long times to calculate...
     // Only a small fraction of the points at the border of the AU is thrown away anyway...
-    if (directions_angles.size() < 4000)
+    if (rot_angles.size() < 4000)
     {
     	// Create no_redundant vectors
-		std::vector <Matrix1D<double> > no_redundant_directions_vector;
-		std::vector <Matrix1D<double> > no_redundant_directions_angles;
+		std::vector <Matrix1D<DOUBLE> > no_redundant_directions_vector;
+		std::vector <DOUBLE> no_redundant_rot_angles;
+		std::vector <DOUBLE> no_redundant_tilt_angles;
 		std::vector <int> no_redundant_directions_ipix;
 
 		// Then check all points versus each other
-		for (long int i = 0; i < directions_angles.size(); i++)
+		for (long int i = 0; i < rot_angles.size(); i++)
 		{
 
-			//if (i%1000==0)
-			//	std::cerr << " i= " << i << " directions_angles.size()= " << directions_angles.size() << " no_redundant_directions_vector.size()= " << no_redundant_directions_vector.size() << std::endl;
-
-			//direction1=(sampling_point_vector[i]).transpose();
 			direction1=directions_vector[i];
 			bool uniq = true;
 
@@ -1381,37 +1344,41 @@ void HealpixSampling::removeSymmetryEquivalentPoints(double max_ang)
 			if (uniq)
 			{
 				no_redundant_directions_vector.push_back(directions_vector[i]);
-				no_redundant_directions_angles.push_back(directions_angles[i]);
+				no_redundant_rot_angles.push_back(rot_angles[i]);
+				no_redundant_tilt_angles.push_back(tilt_angles[i]);
 				no_redundant_directions_ipix.push_back(directions_ipix[i]);
 			}
 		} // for i
 
-		// Now overwrite the directions_angles and directions_vectors with their no_redundant counterparts
-		directions_angles = no_redundant_directions_angles;
+		// Now overwrite the rot/tilt_angles and directions_vectors with their no_redundant counterparts
+		rot_angles = no_redundant_rot_angles;
+		tilt_angles = no_redundant_tilt_angles;
 		directions_ipix = no_redundant_directions_ipix;
     }
 }
 
 void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry,
-        int sym_order, std::vector <Matrix1D<double> >  &directions_vector)
+        int sym_order, std::vector <Matrix1D<DOUBLE> >  &directions_vector)
 {
-    Matrix2D<double>  L(4, 4), R(4, 4);
-    Matrix2D<double>  aux(3, 3);
-    Matrix1D<double>  row1(3), row2(3), row(3);
+    Matrix2D<DOUBLE>  L(4, 4), R(4, 4);
+    Matrix2D<DOUBLE>  aux(3, 3);
+    Matrix1D<DOUBLE>  row1(3), row2(3), row(3);
 
-    std::vector <Matrix1D<double> > no_redundant_directions_vector;
-    std::vector <Matrix1D<double> > no_redundant_directions_angles;
+    std::vector <Matrix1D<DOUBLE> > no_redundant_directions_vector;
+    std::vector <DOUBLE> no_redundant_rot_angles;
+    std::vector <DOUBLE> no_redundant_tilt_angles;
     std::vector <int> no_redundant_directions_ipix;
 
-    double my_dotProduct;
+    DOUBLE my_dotProduct;
     if (symmetry == pg_CN)
     {//OK
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
-            if (XX(directions_angles[i]) >= (-180. / sym_order) &&
-                XX(directions_angles[i]) <= (180. / sym_order))
+            if (rot_angles[i] >= (-180. / sym_order) &&
+                rot_angles[i] <= (180. / sym_order))
             {
-                no_redundant_directions_angles.push_back(directions_angles[i]);
+                no_redundant_rot_angles.push_back(rot_angles[i]);
+                no_redundant_tilt_angles.push_back(tilt_angles[i]);
                 no_redundant_directions_vector.push_back(directions_vector[i]);
                 no_redundant_directions_ipix.push_back(directions_ipix[i]);
             }
@@ -1420,11 +1387,12 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     else if (symmetry == pg_CI  ||
              symmetry == pg_CS )
     {//OK
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
-            if (YY(directions_angles[i]) <= 90)
+            if (tilt_angles[i] <= 90)
             {
-                no_redundant_directions_angles.push_back(directions_angles[i]);
+                no_redundant_rot_angles.push_back(rot_angles[i]);
+                no_redundant_tilt_angles.push_back(tilt_angles[i]);
                 no_redundant_directions_vector.push_back(directions_vector[i]);
                 no_redundant_directions_ipix.push_back(directions_ipix[i]);
             }
@@ -1432,12 +1400,13 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_CNV )
     {//OK
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
-            if (XX(directions_angles[i]) >=    0. / sym_order &&
-                XX(directions_angles[i]) <=  180. / sym_order)
+            if (rot_angles[i] >=    0. / sym_order &&
+                rot_angles[i] <=  180. / sym_order)
             {
-                no_redundant_directions_angles.push_back(directions_angles[i]);
+                no_redundant_rot_angles.push_back(rot_angles[i]);
+                no_redundant_tilt_angles.push_back(tilt_angles[i]);
                 no_redundant_directions_vector.push_back(directions_vector[i]);
                 no_redundant_directions_ipix.push_back(directions_ipix[i]);
             }
@@ -1445,14 +1414,15 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_CNH )
     {//OK
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
-            if (XX(directions_angles[i]) >= -180. / sym_order &&
-                XX(directions_angles[i]) <=  180. / sym_order &&
-                YY(directions_angles[i]) <=    90.
+            if (rot_angles[i] >= -180. / sym_order &&
+                rot_angles[i] <=  180. / sym_order &&
+                tilt_angles[i] <=    90.
                )
             {
-                no_redundant_directions_angles.push_back(directions_angles[i]);
+                no_redundant_rot_angles.push_back(rot_angles[i]);
+                no_redundant_tilt_angles.push_back(tilt_angles[i]);
                 no_redundant_directions_vector.push_back(directions_vector[i]);
                 no_redundant_directions_ipix.push_back(directions_ipix[i]);
             }
@@ -1460,14 +1430,15 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_SN )
     {//OK
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
-            if (XX(directions_angles[i]) >= -180.*2. / sym_order &&
-                XX(directions_angles[i]) <=  180.*2. / sym_order &&
-                YY(directions_angles[i]) <=    90.
+            if (rot_angles[i] >= -180.*2. / sym_order &&
+                rot_angles[i] <=  180.*2. / sym_order &&
+                tilt_angles[i] <=    90.
                )
             {
-                no_redundant_directions_angles.push_back(directions_angles[i]);
+                no_redundant_rot_angles.push_back(rot_angles[i]);
+                no_redundant_tilt_angles.push_back(tilt_angles[i]);
                 no_redundant_directions_vector.push_back(directions_vector[i]);
                 no_redundant_directions_ipix.push_back(directions_ipix[i]);
             }
@@ -1475,14 +1446,15 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_DN )
     {
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
-            if (XX(directions_angles[i]) >= -180. / (sym_order) + 90. &&
-                XX(directions_angles[i]) <=  180. / (sym_order) + 90. &&
-                YY(directions_angles[i]) <=    90.
+            if (rot_angles[i] >= -180. / (sym_order) + 90. &&
+                rot_angles[i] <=  180. / (sym_order) + 90. &&
+                tilt_angles[i] <=    90.
                )
             {
-                no_redundant_directions_angles.push_back(directions_angles[i]);
+                no_redundant_rot_angles.push_back(rot_angles[i]);
+                no_redundant_tilt_angles.push_back(tilt_angles[i]);
                 no_redundant_directions_vector.push_back(directions_vector[i]);
                 no_redundant_directions_ipix.push_back(directions_ipix[i]);
             }
@@ -1490,14 +1462,15 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_DNV )
     {
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
-            if (XX(directions_angles[i]) >=   90.  &&
-                XX(directions_angles[i]) <=  180. / (sym_order) + 90. &&
-                YY(directions_angles[i]) <=    90.
+            if (rot_angles[i] >=   90.  &&
+                rot_angles[i] <=  180. / (sym_order) + 90. &&
+                tilt_angles[i] <=    90.
                )
             {
-                no_redundant_directions_angles.push_back(directions_angles[i]);
+                no_redundant_rot_angles.push_back(rot_angles[i]);
+                no_redundant_tilt_angles.push_back(tilt_angles[i]);
                 no_redundant_directions_vector.push_back(directions_vector[i]);
                 no_redundant_directions_ipix.push_back(directions_ipix[i]);
             }
@@ -1505,14 +1478,15 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_DNH )
     {
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
-            if (XX(directions_angles[i]) >=   90. &&
-                XX(directions_angles[i]) <=  180. / (sym_order) + 90. &&
-                YY(directions_angles[i]) <=   90.
+            if (rot_angles[i] >=   90. &&
+                rot_angles[i] <=  180. / (sym_order) + 90. &&
+                tilt_angles[i] <=   90.
                )
             {
-                no_redundant_directions_angles.push_back(directions_angles[i]);
+                no_redundant_rot_angles.push_back(rot_angles[i]);
+                no_redundant_tilt_angles.push_back(tilt_angles[i]);
                 no_redundant_directions_vector.push_back(directions_vector[i]);
                 no_redundant_directions_ipix.push_back(directions_ipix[i]);
             }
@@ -1520,20 +1494,20 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_T )
     {//OK
-        Matrix1D<double>  _3_fold_axis_1_by_3_fold_axis_2(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_1_by_3_fold_axis_2(3);
         _3_fold_axis_1_by_3_fold_axis_2 = vectorR3(-0.942809, 0., 0.);
         _3_fold_axis_1_by_3_fold_axis_2.selfNormalize();
-        Matrix1D<double>  _3_fold_axis_2_by_3_fold_axis_3(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_2_by_3_fold_axis_3(3);
         _3_fold_axis_2_by_3_fold_axis_3 = vectorR3(0.471405, 0.272165, 0.7698);
         _3_fold_axis_2_by_3_fold_axis_3.selfNormalize();
-        Matrix1D<double>  _3_fold_axis_3_by_3_fold_axis_1(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_3_by_3_fold_axis_1(3);
         _3_fold_axis_3_by_3_fold_axis_1 = vectorR3(0.471404, 0.816497, 0.);
         _3_fold_axis_3_by_3_fold_axis_1.selfNormalize();
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
-            if (XX(directions_angles[i]) >=     90. &&
-                XX(directions_angles[i]) <=   150. ||
-                XX(directions_angles[i]) ==     0
+            if (rot_angles[i] >=     90. &&
+                rot_angles[i] <=   150. ||
+                rot_angles[i] ==     0
                )
                 if (
                     dotProduct(directions_vector[i], _3_fold_axis_1_by_3_fold_axis_2) >= 0 &&
@@ -1541,7 +1515,8 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
                     dotProduct(directions_vector[i], _3_fold_axis_3_by_3_fold_axis_1) >= 0
                 )
                 {
-                    no_redundant_directions_angles.push_back(directions_angles[i]);
+                	no_redundant_rot_angles.push_back(rot_angles[i]);
+                	no_redundant_tilt_angles.push_back(tilt_angles[i]);
                     no_redundant_directions_vector.push_back(directions_vector[i]);
                     no_redundant_directions_ipix.push_back(directions_ipix[i]);
                 }
@@ -1549,20 +1524,20 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_TD )
     {//OK
-        Matrix1D<double>  _2_fold_axis_1_by_3_fold_axis_2(3);
+        Matrix1D<DOUBLE>  _2_fold_axis_1_by_3_fold_axis_2(3);
         _2_fold_axis_1_by_3_fold_axis_2 = vectorR3(-0.942809, 0., 0.);
         _2_fold_axis_1_by_3_fold_axis_2.selfNormalize();
-        Matrix1D<double>  _3_fold_axis_2_by_3_fold_axis_5(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_2_by_3_fold_axis_5(3);
         _3_fold_axis_2_by_3_fold_axis_5 = vectorR3(0.471405, 0.272165, 0.7698);
         _3_fold_axis_2_by_3_fold_axis_5.selfNormalize();
-        Matrix1D<double>  _3_fold_axis_5_by_2_fold_axis_1(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_5_by_2_fold_axis_1(3);
         _3_fold_axis_5_by_2_fold_axis_1 = vectorR3(0., 0.471405, -0.666667);
         _3_fold_axis_5_by_2_fold_axis_1.selfNormalize();
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
-//           if ( XX(directions_angles[i])>=     120. &&
-//                 XX(directions_angles[i])<=   150. ||
-//                 XX(directions_angles[i])==     0
+//           if ( rot_angles[i]>=     120. &&
+//                 rot_angles[i]<=   150. ||
+//                 rot_angles[i]==     0
 //              )
             if (
                 dotProduct(directions_vector[i], _2_fold_axis_1_by_3_fold_axis_2) >= 0 &&
@@ -1570,7 +1545,8 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
                 dotProduct(directions_vector[i], _3_fold_axis_5_by_2_fold_axis_1) >= 0
             )
             {
-                no_redundant_directions_angles.push_back(directions_angles[i]);
+                no_redundant_rot_angles.push_back(rot_angles[i]);
+                no_redundant_tilt_angles.push_back(tilt_angles[i]);
                 no_redundant_directions_vector.push_back(directions_vector[i]);
                 no_redundant_directions_ipix.push_back(directions_ipix[i]);
             }
@@ -1578,20 +1554,20 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_TH )
     {//OK
-        Matrix1D<double>  _3_fold_axis_1_by_2_fold_axis_1(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_1_by_2_fold_axis_1(3);
         _3_fold_axis_1_by_2_fold_axis_1 = vectorR3(-0.816496, 0., 0.);
         _3_fold_axis_1_by_2_fold_axis_1.selfNormalize();
-        Matrix1D<double>  _2_fold_axis_1_by_2_fold_axis_2(3);
+        Matrix1D<DOUBLE>  _2_fold_axis_1_by_2_fold_axis_2(3);
         _2_fold_axis_1_by_2_fold_axis_2 = vectorR3(0.707107, 0.408248, -0.57735);
         _2_fold_axis_1_by_2_fold_axis_2.selfNormalize();
-        Matrix1D<double>  _2_fold_axis_2_by_3_fold_axis_1(3);
+        Matrix1D<DOUBLE>  _2_fold_axis_2_by_3_fold_axis_1(3);
         _2_fold_axis_2_by_3_fold_axis_1 = vectorR3(-0.408248, -0.707107, 0.);
         _2_fold_axis_2_by_3_fold_axis_1.selfNormalize();
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
-//           if ( XX(directions_angles[i])>=     120. &&
-//                 XX(directions_angles[i])<=   150. ||
-//                 XX(directions_angles[i])==     0
+//           if ( rot_angles[i]>=     120. &&
+//                 rot_angles[i]<=   150. ||
+//                 rot_angles[i]==     0
 //              )
             if (
                 dotProduct(directions_vector[i], _3_fold_axis_1_by_2_fold_axis_1) >= 0 &&
@@ -1599,7 +1575,8 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
                 dotProduct(directions_vector[i], _2_fold_axis_2_by_3_fold_axis_1) >= 0
             )
             {
-                no_redundant_directions_angles.push_back(directions_angles[i]);
+                no_redundant_rot_angles.push_back(rot_angles[i]);
+                no_redundant_tilt_angles.push_back(tilt_angles[i]);
                 no_redundant_directions_vector.push_back(directions_vector[i]);
                 no_redundant_directions_ipix.push_back(directions_ipix[i]);
             }
@@ -1607,21 +1584,21 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_O )
     {//OK
-        Matrix1D<double>  _3_fold_axis_1_by_3_fold_axis_2(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_1_by_3_fold_axis_2(3);
         _3_fold_axis_1_by_3_fold_axis_2 = vectorR3(0., -1., 1.);
         _3_fold_axis_1_by_3_fold_axis_2.selfNormalize();
-        Matrix1D<double>  _3_fold_axis_2_by_4_fold_axis(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_2_by_4_fold_axis(3);
         _3_fold_axis_2_by_4_fold_axis = vectorR3(1., 1., 0.);
         _3_fold_axis_2_by_4_fold_axis.selfNormalize();
-        Matrix1D<double>  _4_fold_axis_by_3_fold_axis_1(3);
+        Matrix1D<DOUBLE>  _4_fold_axis_by_3_fold_axis_1(3);
         _4_fold_axis_by_3_fold_axis_1 = vectorR3(-1., 1., 0.);
         _4_fold_axis_by_3_fold_axis_1.selfNormalize();
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
-            if ((XX(directions_angles[i]) >=   45. &&
-                 XX(directions_angles[i]) <=  135. &&
-                 YY(directions_angles[i]) <=  90.) ||
-                XX(directions_angles[i]) ==  0.
+            if ((rot_angles[i] >=   45. &&
+                 rot_angles[i] <=  135. &&
+                 tilt_angles[i] <=  90.) ||
+                rot_angles[i] ==  0.
                )
                 if (
                     dotProduct(directions_vector[i], _3_fold_axis_1_by_3_fold_axis_2) >= 0 &&
@@ -1629,7 +1606,8 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
                     dotProduct(directions_vector[i], _4_fold_axis_by_3_fold_axis_1) >= 0
                 )
                 {
-                    no_redundant_directions_angles.push_back(directions_angles[i]);
+                	no_redundant_rot_angles.push_back(rot_angles[i]);
+                	no_redundant_tilt_angles.push_back(tilt_angles[i]);
                     no_redundant_directions_vector.push_back(directions_vector[i]);
                     no_redundant_directions_ipix.push_back(directions_ipix[i]);
                 }
@@ -1637,27 +1615,28 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_OH )
     {//OK
-        Matrix1D<double>  _3_fold_axis_1_by_3_fold_axis_2(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_1_by_3_fold_axis_2(3);
         _3_fold_axis_1_by_3_fold_axis_2 = vectorR3(0., -1., 1.);
         _3_fold_axis_1_by_3_fold_axis_2.selfNormalize();
-        Matrix1D<double>  _3_fold_axis_2_by_4_fold_axis(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_2_by_4_fold_axis(3);
         _3_fold_axis_2_by_4_fold_axis = vectorR3(1., 1., 0.);
         _3_fold_axis_2_by_4_fold_axis.selfNormalize();
-        Matrix1D<double>  _4_fold_axis_by_3_fold_axis_1(3);
+        Matrix1D<DOUBLE>  _4_fold_axis_by_3_fold_axis_1(3);
         _4_fold_axis_by_3_fold_axis_1 = vectorR3(-1., 1., 0.);
         _4_fold_axis_by_3_fold_axis_1.selfNormalize();
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
-            if (XX(directions_angles[i]) >=   90. &&
-                XX(directions_angles[i]) <=  135. &&
-                YY(directions_angles[i]) <=  90.)
+            if (rot_angles[i] >=   90. &&
+                rot_angles[i] <=  135. &&
+                tilt_angles[i] <=  90.)
                 if (
                     dotProduct(directions_vector[i], _3_fold_axis_1_by_3_fold_axis_2) >= 0 &&
                     dotProduct(directions_vector[i], _3_fold_axis_2_by_4_fold_axis) >= 0 &&
                     dotProduct(directions_vector[i], _4_fold_axis_by_3_fold_axis_1) >= 0
                 )
                 {
-                    no_redundant_directions_angles.push_back(directions_angles[i]);
+                	no_redundant_rot_angles.push_back(rot_angles[i]);
+                	no_redundant_tilt_angles.push_back(tilt_angles[i]);
                     no_redundant_directions_vector.push_back(directions_vector[i]);
                     no_redundant_directions_ipix.push_back(directions_ipix[i]);
                 }
@@ -1665,20 +1644,20 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_I || symmetry  == pg_I2)
     {//OK
-        Matrix1D<double>  _5_fold_axis_1_by_5_fold_axis_2(3);
+        Matrix1D<DOUBLE>  _5_fold_axis_1_by_5_fold_axis_2(3);
         _5_fold_axis_1_by_5_fold_axis_2 = vectorR3(0., 1., 0.);
         _5_fold_axis_1_by_5_fold_axis_2.selfNormalize();
-        Matrix1D<double>  _5_fold_axis_2_by_3_fold_axis(3);
+        Matrix1D<DOUBLE>  _5_fold_axis_2_by_3_fold_axis(3);
         _5_fold_axis_2_by_3_fold_axis = vectorR3(-0.4999999839058737,
                                                  -0.8090170074556163,
                                                   0.3090169861701543);
         _5_fold_axis_2_by_3_fold_axis.selfNormalize();
-        Matrix1D<double>  _3_fold_axis_by_5_fold_axis_1(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_by_5_fold_axis_1(3);
         _3_fold_axis_by_5_fold_axis_1 = vectorR3(0.4999999839058737,
                                                 -0.8090170074556163,
                                                  0.3090169861701543);
         _3_fold_axis_by_5_fold_axis_1.selfNormalize();
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
             if (
                     dotProduct(directions_vector[i], _5_fold_axis_1_by_5_fold_axis_2) >= 0 &&
@@ -1686,7 +1665,8 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
                     dotProduct(directions_vector[i], _3_fold_axis_by_5_fold_axis_1) >= 0
                )
                 {
-                    no_redundant_directions_angles.push_back(directions_angles[i]);
+					no_redundant_rot_angles.push_back(rot_angles[i]);
+					no_redundant_tilt_angles.push_back(tilt_angles[i]);
                     no_redundant_directions_vector.push_back(directions_vector[i]);
                     no_redundant_directions_ipix.push_back(directions_ipix[i]);
                 }
@@ -1694,23 +1674,23 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_I1)
     {//OK
-        Matrix2D<double>  A(3, 3);
+        Matrix2D<DOUBLE>  A(3, 3);
 	    Euler_angles2matrix(0, 90, 0, A);
-        Matrix1D<double>  _5_fold_axis_1_by_5_fold_axis_2(3);
+        Matrix1D<DOUBLE>  _5_fold_axis_1_by_5_fold_axis_2(3);
         _5_fold_axis_1_by_5_fold_axis_2 = A * vectorR3(0., 1., 0.);
         _5_fold_axis_1_by_5_fold_axis_2.selfNormalize();
-        Matrix1D<double>  _5_fold_axis_2_by_3_fold_axis(3);
+        Matrix1D<DOUBLE>  _5_fold_axis_2_by_3_fold_axis(3);
         _5_fold_axis_2_by_3_fold_axis = A * vectorR3(-0.4999999839058737,
                                                  -0.8090170074556163,
                                                   0.3090169861701543);
         _5_fold_axis_2_by_3_fold_axis.selfNormalize();
-        Matrix1D<double>  _3_fold_axis_by_5_fold_axis_1(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_by_5_fold_axis_1(3);
         _3_fold_axis_by_5_fold_axis_1 = A * vectorR3(0.4999999839058737,
                                                 -0.8090170074556163,
                                                  0.3090169861701543);
         _3_fold_axis_by_5_fold_axis_1.selfNormalize();
 
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
             if (
                     dotProduct(directions_vector[i], _5_fold_axis_1_by_5_fold_axis_2) >= 0 &&
@@ -1718,7 +1698,8 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
                     dotProduct(directions_vector[i], _3_fold_axis_by_5_fold_axis_1) >= 0
             )
             {
-                no_redundant_directions_angles.push_back(directions_angles[i]);
+                no_redundant_rot_angles.push_back(rot_angles[i]);
+                no_redundant_tilt_angles.push_back(tilt_angles[i]);
                 no_redundant_directions_vector.push_back(directions_vector[i]);
                 no_redundant_directions_ipix.push_back(directions_ipix[i]);
             }
@@ -1726,23 +1707,23 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_I3)
     {//OK
-        Matrix2D<double>  A(3, 3);
+        Matrix2D<DOUBLE>  A(3, 3);
 	    Euler_angles2matrix(0,31.7174745559,0, A);
-        Matrix1D<double>  _5_fold_axis_1_by_5_fold_axis_2(3);
+        Matrix1D<DOUBLE>  _5_fold_axis_1_by_5_fold_axis_2(3);
         _5_fold_axis_1_by_5_fold_axis_2 = A * vectorR3(0., 1., 0.);
         _5_fold_axis_1_by_5_fold_axis_2.selfNormalize();
-        Matrix1D<double>  _5_fold_axis_2_by_3_fold_axis(3);
+        Matrix1D<DOUBLE>  _5_fold_axis_2_by_3_fold_axis(3);
         _5_fold_axis_2_by_3_fold_axis = A * vectorR3(-0.4999999839058737,
                                                  -0.8090170074556163,
                                                   0.3090169861701543);
         _5_fold_axis_2_by_3_fold_axis.selfNormalize();
-        Matrix1D<double>  _3_fold_axis_by_5_fold_axis_1(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_by_5_fold_axis_1(3);
         _3_fold_axis_by_5_fold_axis_1 = A * vectorR3(0.4999999839058737,
                                                 -0.8090170074556163,
                                                  0.3090169861701543);
         _3_fold_axis_by_5_fold_axis_1.selfNormalize();
 
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
             if (
                     dotProduct(directions_vector[i], _5_fold_axis_1_by_5_fold_axis_2) >= 0 &&
@@ -1750,7 +1731,8 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
                     dotProduct(directions_vector[i], _3_fold_axis_by_5_fold_axis_1) >= 0
             )
             {
-                no_redundant_directions_angles.push_back(directions_angles[i]);
+                no_redundant_rot_angles.push_back(rot_angles[i]);
+                no_redundant_tilt_angles.push_back(tilt_angles[i]);
                 no_redundant_directions_vector.push_back(directions_vector[i]);
                 no_redundant_directions_ipix.push_back(directions_ipix[i]);
             }
@@ -1758,23 +1740,23 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_I4)
     {//OK
-        Matrix2D<double>  A(3, 3);
+        Matrix2D<DOUBLE>  A(3, 3);
 	    Euler_angles2matrix(0,-31.7174745559,0, A);
-        Matrix1D<double>  _5_fold_axis_1_by_5_fold_axis_2(3);
+        Matrix1D<DOUBLE>  _5_fold_axis_1_by_5_fold_axis_2(3);
         _5_fold_axis_1_by_5_fold_axis_2 = A * vectorR3(0., 0., 1.);
         _5_fold_axis_1_by_5_fold_axis_2.selfNormalize();
-        Matrix1D<double>  _5_fold_axis_2_by_3_fold_axis(3);
+        Matrix1D<DOUBLE>  _5_fold_axis_2_by_3_fold_axis(3);
         _5_fold_axis_2_by_3_fold_axis = A * vectorR3(0.187592467856686,
                                         -0.303530987314591,
                                         -0.491123477863004);
         _5_fold_axis_2_by_3_fold_axis.selfNormalize();
-        Matrix1D<double>  _3_fold_axis_by_5_fold_axis_1(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_by_5_fold_axis_1(3);
         _3_fold_axis_by_5_fold_axis_1 = A * vectorR3(0.187592467856686,
                                         0.303530987314591,
                                         -0.491123477863004);
         _3_fold_axis_by_5_fold_axis_1.selfNormalize();
 
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
             if (
                 dotProduct(directions_vector[i],
@@ -1785,7 +1767,8 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
 		           _5_fold_axis_1_by_5_fold_axis_2) <= 0
             )
             {
-                no_redundant_directions_angles.push_back(directions_angles[i]);
+                no_redundant_rot_angles.push_back(rot_angles[i]);
+                no_redundant_tilt_angles.push_back(tilt_angles[i]);
                 no_redundant_directions_vector.push_back(directions_vector[i]);
                 no_redundant_directions_ipix.push_back(directions_ipix[i]);
             }
@@ -1798,23 +1781,23 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_IH || symmetry  == pg_I2H)
     {//OK
-        Matrix1D<double>  _5_fold_axis_1_by_5_fold_axis_2(3);
+        Matrix1D<DOUBLE>  _5_fold_axis_1_by_5_fold_axis_2(3);
         _5_fold_axis_1_by_5_fold_axis_2 = vectorR3(0., 1., 0.);
         _5_fold_axis_1_by_5_fold_axis_2.selfNormalize();
-        Matrix1D<double>  _5_fold_axis_2_by_3_fold_axis(3);
+        Matrix1D<DOUBLE>  _5_fold_axis_2_by_3_fold_axis(3);
         _5_fold_axis_2_by_3_fold_axis = vectorR3(-0.4999999839058737,
                                                  -0.8090170074556163,
                                                   0.3090169861701543);
         _5_fold_axis_2_by_3_fold_axis.selfNormalize();
-        Matrix1D<double>  _3_fold_axis_by_5_fold_axis_1(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_by_5_fold_axis_1(3);
         _3_fold_axis_by_5_fold_axis_1 = vectorR3(0.4999999839058737,
                                                 -0.8090170074556163,
                                                  0.3090169861701543);
         _3_fold_axis_by_5_fold_axis_1.selfNormalize();
-        Matrix1D<double>  _3_fold_axis_by_2_fold_axis(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_by_2_fold_axis(3);
         _3_fold_axis_by_2_fold_axis =  vectorR3(1.,0.,0.);
         _3_fold_axis_by_2_fold_axis.selfNormalize();
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
             if (
                     dotProduct(directions_vector[i], _5_fold_axis_1_by_5_fold_axis_2) >= 0 &&
@@ -1822,7 +1805,8 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
                     dotProduct(directions_vector[i], _3_fold_axis_by_2_fold_axis) >= 0
                )
                 {
-                    no_redundant_directions_angles.push_back(directions_angles[i]);
+					no_redundant_rot_angles.push_back(rot_angles[i]);
+					no_redundant_tilt_angles.push_back(tilt_angles[i]);
                     no_redundant_directions_vector.push_back(directions_vector[i]);
                     no_redundant_directions_ipix.push_back(directions_ipix[i]);
                 }
@@ -1830,25 +1814,25 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_I1H)
     {//OK
-        Matrix2D<double>  A(3, 3);
+        Matrix2D<DOUBLE>  A(3, 3);
 	    Euler_angles2matrix(0, 90, 0, A);
-        Matrix1D<double>  _5_fold_axis_1_by_5_fold_axis_2(3);
+        Matrix1D<DOUBLE>  _5_fold_axis_1_by_5_fold_axis_2(3);
         _5_fold_axis_1_by_5_fold_axis_2 = A * vectorR3(0., 1., 0.);
         _5_fold_axis_1_by_5_fold_axis_2.selfNormalize();
-        Matrix1D<double>  _5_fold_axis_2_by_3_fold_axis(3);
+        Matrix1D<DOUBLE>  _5_fold_axis_2_by_3_fold_axis(3);
         _5_fold_axis_2_by_3_fold_axis = A * vectorR3(-0.4999999839058737,
                                                  -0.8090170074556163,
                                                   0.3090169861701543);
         _5_fold_axis_2_by_3_fold_axis.selfNormalize();
-        Matrix1D<double>  _3_fold_axis_by_5_fold_axis_1(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_by_5_fold_axis_1(3);
         _3_fold_axis_by_5_fold_axis_1 = A * vectorR3(0.4999999839058737,
                                                 -0.8090170074556163,
                                                  0.3090169861701543);
         _3_fold_axis_by_5_fold_axis_1.selfNormalize();
-        Matrix1D<double>  _3_fold_axis_by_2_fold_axis(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_by_2_fold_axis(3);
         _3_fold_axis_by_2_fold_axis =  A * vectorR3(1.,0.,0.);
         _3_fold_axis_by_2_fold_axis.selfNormalize();
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
             if (
                     dotProduct(directions_vector[i], _5_fold_axis_1_by_5_fold_axis_2) >= 0 &&
@@ -1856,7 +1840,8 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
                     dotProduct(directions_vector[i], _3_fold_axis_by_2_fold_axis) >= 0
                )
                 {
-                    no_redundant_directions_angles.push_back(directions_angles[i]);
+					no_redundant_rot_angles.push_back(rot_angles[i]);
+					no_redundant_tilt_angles.push_back(tilt_angles[i]);
                     no_redundant_directions_vector.push_back(directions_vector[i]);
                     no_redundant_directions_ipix.push_back(directions_ipix[i]);
                 }
@@ -1864,25 +1849,25 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_I3H)
     {//OK
-        Matrix2D<double>  A(3, 3);
+        Matrix2D<DOUBLE>  A(3, 3);
 	    Euler_angles2matrix(0,31.7174745559,0, A);
-        Matrix1D<double>  _5_fold_axis_1_by_5_fold_axis_2(3);
+        Matrix1D<DOUBLE>  _5_fold_axis_1_by_5_fold_axis_2(3);
         _5_fold_axis_1_by_5_fold_axis_2 = A * vectorR3(0., 0., 1.);
         _5_fold_axis_1_by_5_fold_axis_2.selfNormalize();
-        Matrix1D<double>  _5_fold_axis_2_by_3_fold_axis(3);
+        Matrix1D<DOUBLE>  _5_fold_axis_2_by_3_fold_axis(3);
         _5_fold_axis_2_by_3_fold_axis = A * vectorR3(0.187592467856686,
                                         -0.303530987314591,
                                         -0.491123477863004);
         _5_fold_axis_2_by_3_fold_axis.selfNormalize();
-        Matrix1D<double>  _3_fold_axis_by_5_fold_axis_1(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_by_5_fold_axis_1(3);
         _3_fold_axis_by_5_fold_axis_1 = A * vectorR3(0.187592467856686,
                                         0.303530987314591,
                                         -0.491123477863004);
         _3_fold_axis_by_5_fold_axis_1.selfNormalize();
-        Matrix1D<double>  _3_fold_axis_by_2_fold_axis(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_by_2_fold_axis(3);
         _3_fold_axis_by_2_fold_axis = vectorR3(0.,1.,0.);
         _3_fold_axis_by_2_fold_axis.selfNormalize();
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
             if (
                 dotProduct(directions_vector[i],
@@ -1895,7 +1880,8 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
                    _3_fold_axis_by_2_fold_axis)     >= 0
             )
             {
-                no_redundant_directions_angles.push_back(directions_angles[i]);
+                no_redundant_rot_angles.push_back(rot_angles[i]);
+                no_redundant_tilt_angles.push_back(tilt_angles[i]);
                 no_redundant_directions_vector.push_back(directions_vector[i]);
                 no_redundant_directions_ipix.push_back(directions_ipix[i]);
             }
@@ -1903,25 +1889,25 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
     else if (symmetry  == pg_I4H)
     {//OK
-        Matrix2D<double>  A(3, 3);
+        Matrix2D<DOUBLE>  A(3, 3);
 	Euler_angles2matrix(0,-31.7174745559,0, A);
-        Matrix1D<double>  _5_fold_axis_1_by_5_fold_axis_2(3);
+        Matrix1D<DOUBLE>  _5_fold_axis_1_by_5_fold_axis_2(3);
         _5_fold_axis_1_by_5_fold_axis_2 = A * vectorR3(0., 0., 1.);
         _5_fold_axis_1_by_5_fold_axis_2.selfNormalize();
-        Matrix1D<double>  _5_fold_axis_2_by_3_fold_axis(3);
+        Matrix1D<DOUBLE>  _5_fold_axis_2_by_3_fold_axis(3);
         _5_fold_axis_2_by_3_fold_axis = A * vectorR3(0.187592467856686,
                                         -0.303530987314591,
                                         -0.491123477863004);
         _5_fold_axis_2_by_3_fold_axis.selfNormalize();
-        Matrix1D<double>  _3_fold_axis_by_5_fold_axis_1(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_by_5_fold_axis_1(3);
         _3_fold_axis_by_5_fold_axis_1 = A * vectorR3(0.187592467856686,
                                         0.303530987314591,
                                         -0.491123477863004);
         _3_fold_axis_by_5_fold_axis_1.selfNormalize();
-        Matrix1D<double>  _3_fold_axis_by_2_fold_axis(3);
+        Matrix1D<DOUBLE>  _3_fold_axis_by_2_fold_axis(3);
         _3_fold_axis_by_2_fold_axis = vectorR3(0.,1.,0.);
         _3_fold_axis_by_2_fold_axis.selfNormalize();
-        for (long int i = 0; i < directions_angles.size(); i++)
+        for (long int i = 0; i < rot_angles.size(); i++)
         {
             if (
                 dotProduct(directions_vector[i],
@@ -1934,7 +1920,8 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
                    _3_fold_axis_by_2_fold_axis)     >= 0
             )
             {
-                no_redundant_directions_angles.push_back(directions_angles[i]);
+                no_redundant_rot_angles.push_back(rot_angles[i]);
+                no_redundant_tilt_angles.push_back(tilt_angles[i]);
                 no_redundant_directions_vector.push_back(directions_vector[i]);
                 no_redundant_directions_ipix.push_back(directions_ipix[i]);
             }
@@ -1952,8 +1939,9 @@ void HealpixSampling::removeSymmetryEquivalentPointsGeometric(const int symmetry
     }
 
 
-    // Now overwrite the directions_angles and directions_vectors with their no_redundant counterparts
-    directions_angles = no_redundant_directions_angles;
+    // Now overwrite the rot/tilt_angles and directions_vectors with their no_redundant counterparts
+    rot_angles = no_redundant_rot_angles;
+    tilt_angles = no_redundant_tilt_angles;
     directions_vector = no_redundant_directions_vector;
     directions_ipix = no_redundant_directions_ipix;
 
diff --git a/src/healpix_sampling.h b/src/healpix_sampling.h
index 9d5d873..3d99dba 100644
--- a/src/healpix_sampling.h
+++ b/src/healpix_sampling.h
@@ -41,14 +41,14 @@ public:
     Healpix_Base healpix_base;
 
     /** Random perturbation */
-    double random_perturbation;
+    DOUBLE random_perturbation;
 
     /** Amount of random perturbation */
-    double perturbation_factor;
+    DOUBLE perturbation_factor;
 
     /** In-plane (psi-angle) sampling rate
      */
-    double psi_step;
+    DOUBLE psi_step;
 
     /** Healpix order */
     int healpix_order;
@@ -61,7 +61,7 @@ public:
 
     /* Translational search range and sampling rate
      */
-    double offset_range, offset_step;
+    DOUBLE offset_range, offset_step;
 
     /** Flag whether this is a real 3D sampling */
     bool is_3D;
@@ -73,60 +73,46 @@ public:
     FileName fn_sym;
 
     /** List of symmetry operators */
-    std::vector <Matrix2D<double> > R_repository, L_repository;
+    std::vector <Matrix2D<DOUBLE> > R_repository, L_repository;
 
     /** Two numbers that describe the symmetry group */
     int pgGroup;
     int pgOrder;
 
     /** Limited tilt angle range */
-    double limit_tilt;
+    DOUBLE limit_tilt;
 
     /** vector with the original pixel number in the healpix object */
     std::vector<int> directions_ipix;
 
     /** vector with sampling points described by angles */
-    std::vector<Matrix1D<double> > directions_angles;
+    std::vector<DOUBLE > rot_angles, tilt_angles;
 
     /** vector with the psi-samples */
-    std::vector<double> psi_angles;
+    std::vector<DOUBLE> psi_angles;
 
     /** vector with the X,Y(,Z)-translations */
-    std::vector<Matrix1D<double> > translations;
-
-    /** vector with pointers to the (rot,tilt) pairs (directions) that have non-zero prior probability */
-    std::vector<int> pointer_dir_nonzeroprior;
-
-    /** vector with pointers to the psi-samples that have non-zero prior probability */
-    std::vector<int> pointer_psi_nonzeroprior;
-
-//TMP DEBUGGING: normally protected!
-public:
-    /** vector with the prior probabilities for those directions that have non-zero prior probability*/
-    std::vector<double> directions_prior;
-
-    /** vector with the prior probabilities for those psi-angles that have non-zero prior probability*/
-    std::vector<double> psi_prior;
-
+    std::vector<DOUBLE> translations_x, translations_y, translations_z;
 
 
 public:
 
     // Empty constructor
-    HealpixSampling() {}
+    HealpixSampling()
+    {
+    	clear();
+    }
 
     // Destructor
     ~HealpixSampling()
     {
     	directions_ipix.clear();
-    	directions_angles.clear();
+    	rot_angles.clear();
+    	tilt_angles.clear();
     	psi_angles.clear();
-    	pointer_dir_nonzeroprior.clear();
-    	pointer_psi_nonzeroprior.clear();
-    	directions_prior.clear();
-    	psi_prior.clear();
-    	translations.clear();
-
+    	translations_x.clear();
+    	translations_y.clear();
+    	translations_z.clear();
     }
 
     // Start from all empty vectors and meaningless parameters
@@ -173,37 +159,34 @@ public:
     void write(FileName fn_out);
 
     /* Set the non-oversampled list of translations */
-    void setTranslations(double offset_step = -1., double offset_range = -1.);
+    void setTranslations(DOUBLE offset_step = -1., DOUBLE offset_range = -1.);
 
-    /* Set only a single translation */
-    void setOneTranslation(Matrix1D<double> offset);
+    /* Add a single translation */
+    void addOneTranslation(DOUBLE offset_x, DOUBLE offset_y, DOUBLE offset_z, bool do_clear = false);
 
     /* Set the non-oversampled lists of directions and in-plane rotations */
-    void setOrientations(int _order = -1, double _psi_step = -1.);
+    void setOrientations(int _order = -1, DOUBLE _psi_step = -1.);
 
-    /* Set only a single orientation */
-    void setOneOrientation(double rot, double tilt, double psi);
+    /* Add a single orientation */
+    void addOneOrientation(DOUBLE rot, DOUBLE tilt, DOUBLE psi, bool do_clear = false);
 
 
     /* Write all orientations as a sphere in a bild file
      * Mainly useful for debugging */
-    void writeAllOrientationsToBild(FileName fn_bild, std::string rgb = "1 0 0", double size = 0.025);
-    void writeNonZeroPriorOrientationsToBild(FileName fn_bild, double rot_prior, double tilt_prior, std::string rgb = "0 0 1", double size = 0.025);
+    void writeAllOrientationsToBild(FileName fn_bild, std::string rgb = "1 0 0", DOUBLE size = 0.025);
+    void writeNonZeroPriorOrientationsToBild(FileName fn_bild, DOUBLE rot_prior, DOUBLE tilt_prior,
+    		std::vector<int> &pointer_dir_nonzeroprior, std::string rgb = "0 0 1", DOUBLE size = 0.025);
 
     /* Select all orientations with zero prior probabilities
      * store all these in the vectors pointer_dir_nonzeroprior and pointer_psi_nonzeroprior
      * Also precalculate their prior probabilities and store in directions_prior and psi_prior
      */
     void selectOrientationsWithNonZeroPriorProbability(
-    		double prior_rot, double prior_tilt, double prior_psi,
-    		double sigma_rot, double sigma_tilt, double sigma_psi,
-    		double sigma_cutoff = 3.);
-
-    /* Randomly reject part of te non-zero prior probabilities, so that the optimization no longer follows the steepest downward gradient
-     * This procedure was inspired by Hans Elmlund's PRIME algorithm.
-     */
-    void randomSelectionNonZeroPriorProbability(double fraction_to_keep);
-
+    		DOUBLE prior_rot, DOUBLE prior_tilt, DOUBLE prior_psi,
+    		DOUBLE sigma_rot, DOUBLE sigma_tilt, DOUBLE sigma_psi,
+    		std::vector<int> &pointer_dir_nonzeroprior, std::vector<DOUBLE> &directions_prior,
+    		std::vector<int> &pointer_psi_nonzeroprior, std::vector<DOUBLE> &psi_prior,
+    		DOUBLE sigma_cutoff = 3.);
 
     /** Get the symmetry group of this sampling object
      */
@@ -217,29 +200,29 @@ public:
 
     /** The geometrical considerations about the symmetry below require that rot = [-180,180] and tilt [0,180]
      */
-    void checkDirection(double &rot, double &tilt);
+    void checkDirection(DOUBLE &rot, DOUBLE &tilt);
 
     /* Get the rot and tilt angles in the center of the ipix'th HEALPix sampling pixel
      * This involves calculations in the HEALPix library
      */
-    void getDirectionFromHealPix(long int ipix, double &rot, double &tilt);
+    void getDirectionFromHealPix(long int ipix, DOUBLE &rot, DOUBLE &tilt);
 
     /* Get the translational sampling step in pixels */
-    double getTranslationalSampling(int adaptive_oversampling = 0);
+    DOUBLE getTranslationalSampling(int adaptive_oversampling = 0);
 
     /* Get approximate angular sampling in degrees for any adaptive oversampling
      */
-    double getAngularSampling(int adaptive_oversampling = 0);
+    DOUBLE getAngularSampling(int adaptive_oversampling = 0);
 
     /* Get the number of symmetry-unique sampling points
      * Note that because of symmetry-equivalence removal this number is not the number of original HEALPix pixels
      * In the case of orientational priors, the number of directions with non-zero prior probability is returned
      */
-    long int NrDirections(int oversampling_order = 0, bool include_zeroprior = false);
+    long int NrDirections(int oversampling_order = 0, const std::vector<int> *pointer_dir_nonzeroprior = NULL);
 
     /* Get the number of in-plane (psi-angle) sampling points
      */
-    long int NrPsiSamplings(int oversampling_order = 0, bool include_zeroprior = false);
+    long int NrPsiSamplings(int oversampling_order = 0, const std::vector<int> *pointer_psi_nonzeroprior = NULL);
 
     /* Get the number of in-plane translational sampling points
      */
@@ -247,7 +230,9 @@ public:
 
     /* Get the total number of (oversampled) sampling points, i.e. all (rot, tilt, psi, xoff, yoff) quintets
     */
-    long int NrSamplingPoints(int oversampling_order = 0, bool include_zeroprior = false);
+    long int NrSamplingPoints(int oversampling_order = 0,
+    		const std::vector<int> *pointer_dir_nonzeroprior = NULL,
+    		const std::vector<int> *pointer_psi_nonzeroprior = NULL);
 
     /* How often is each orientation oversampled? */
     int oversamplingFactorOrientations(int oversampling_order);
@@ -259,15 +244,15 @@ public:
      * This does not involve calculations in the HEALPix library
      * Note that because of symmetry-equivalence removal idir no longer corresponds to the HEALPix pixel number
      */
-    void getDirection(long int idir, double &rot, double &tilt);
+    void getDirection(long int idir, DOUBLE &rot, DOUBLE &tilt);
 
     /* Get the value for the ipsi'th precalculated psi angle
      */
-    void getPsiAngle(long int ipsi, double &psi);
+    void getPsiAngle(long int ipsi, DOUBLE &psi);
 
     /* Get the value for the itrans'th precalculated translations
      */
-    void getTranslation(long int itrans, Matrix1D<double> &trans);
+    void getTranslation(long int itrans, DOUBLE &trans_x, DOUBLE &trans_y, DOUBLE &trans_z);
 
     /* Get the position of this sampling point in the original array */
     long int getPositionSamplingPoint(int iclass, long int idir, long int ipsi, long int itrans);
@@ -283,7 +268,9 @@ public:
      * etc.
      */
     void getTranslations(long int itrans, int oversampling_order,
-								    std::vector<Matrix1D<double> > &my_translations);
+    		std::vector<DOUBLE > &my_translations_x,
+    		std::vector<DOUBLE > &my_translations_y,
+    		std::vector<DOUBLE > &my_translations_z);
 
     /* Get the vectors of (rot, tilt, psi) angle triplets for a more finely (oversampled) sampling
      * The oversampling_order is the difference in order of the original (coarse) and the oversampled (fine) sampling
@@ -295,19 +282,10 @@ public:
      * If only_nonzero_prior is true, then only the orientations with non-zero prior probabilities will be returned
      * This is for local angular searches
      */
-    void getOrientations(long int idir, long int ipsi, int oversampling_order, std::vector<Matrix1D<double> > &my_orientations);
-
-    /** Get the prior probability for this orientation
-     */
-    double getPriorProbability(long int idir, long int ipsi);
-
-    /** Get the number of the original direction from the ones with also non-zero prior probability
-     */
-    long int getDirectionNumberAlsoZeroPrior(long int idir);
-
-    /** Get the number of the original direction from the ones with also non-zero prior probability
-     */
-    long int getPsiNumberAlsoZeroPrior(long int ipsi);
+    void getOrientations(long int idir, long int ipsi, int oversampling_order,
+    		std::vector<DOUBLE > &my_rot, std::vector<DOUBLE > &my_tilt, std::vector<DOUBLE > &my_psi,
+    		std::vector<int> &pointer_dir_nonzeroprior, std::vector<DOUBLE> &directions_prior,
+    		std::vector<int> &pointer_psi_nonzeroprior, std::vector<DOUBLE> &psi_prior);
 
     /* Gets the vector of psi angles for a more finely (oversampled) sampling and
      * pushes each instance back into the oversampled_orientations vector with the given rot and tilt
@@ -318,19 +296,20 @@ public:
      * etc.
      */
     void pushbackOversampledPsiAngles(long int ipsi, int oversampling_order,
-    		double rot, double tilt, std::vector<Matrix1D<double> > &oversampled_orientations);
+    		DOUBLE rot, DOUBLE tilt, std::vector<DOUBLE> &oversampled_rot,
+    		std::vector<DOUBLE> &oversampled_tilt, std::vector<DOUBLE> &oversampled_psi);
 
     /* Calculate an angular distance between two sets of Euler angles */
-    double calculateAngularDistance(double rot1, double tilt1, double psi1,
-    		double rot2, double tilt2, double psi2);
+    DOUBLE calculateAngularDistance(DOUBLE rot1, DOUBLE tilt1, DOUBLE psi1,
+    		DOUBLE rot2, DOUBLE tilt2, DOUBLE psi2);
 
     /* Write a BILD file describing the angular distribution
      *  R determines the radius of the sphere on which cylinders will be placed
      *  Rmax_frac determines the length of the longest cylinder (relative to R, 0.2 + +20%)
      *  width_frac determines how broad each cylinder is. frac=1 means they touch each other
      * */
-    void writeBildFileOrientationalDistribution(MultidimArray<double> &pdf_direction,
-    		FileName &fn_bild, double R, double offset = 0., double Rmax_frac = 0.3, double width_frac = 0.5);
+    void writeBildFileOrientationalDistribution(MultidimArray<DOUBLE> &pdf_direction,
+    		FileName &fn_bild, DOUBLE R, DOUBLE offset = 0., DOUBLE Rmax_frac = 0.3, DOUBLE width_frac = 0.5);
 
 private:
 
@@ -348,12 +327,12 @@ private:
         If this distance is less than 0.8 times the angular sampling, the point is deleted
         This cares care of sampling points near the edge of the geometrical considerations
     */
-    void removeSymmetryEquivalentPoints(double max_ang);
+    void removeSymmetryEquivalentPoints(DOUBLE max_ang);
 
     /* eliminate symmetry-related points based on simple geometrical considerations,
         symmetry group, symmetry order */
     void removeSymmetryEquivalentPointsGeometric(const int symmetry, int sym_order,
-												 std::vector <Matrix1D<double> >  &sampling_points_vector);
+												 std::vector <Matrix1D<DOUBLE> >  &sampling_points_vector);
 
 
 
diff --git a/src/image.cpp b/src/image.cpp
index 96cf559..21f495d 100644
--- a/src/image.cpp
+++ b/src/image.cpp
@@ -30,7 +30,7 @@ unsigned long  gettypesize(DataType type)
         case UShort: case Short: size = sizeof(short); break;
         case UInt:	 case Int:   size = sizeof(int); break;
         case Float:              size = sizeof(float); break;
-        case Double:             size = sizeof(double); break;
+        case Double:             size = sizeof(DOUBLE); break;
         case Bool:				  size = sizeof(bool); break;
         default: size = 0;
     }
@@ -71,27 +71,31 @@ int datatypeString2Int(std::string s)
 }
 
 // Some image-specific operations
-void normalise(Image<double> &I, int bg_radius, double white_dust_stddev, double black_dust_stddev)
+void normalise(Image<DOUBLE> &I, int bg_radius, DOUBLE white_dust_stddev, DOUBLE black_dust_stddev, bool do_ramp)
 {
 	int bg_radius2 = bg_radius * bg_radius;
-	double avg, stddev;
+	DOUBLE avg, stddev;
 
 	if (2*bg_radius > XSIZE(I()))
 		REPORT_ERROR("normalise ERROR: 2*bg_radius is larger than image size!");
 
-	// Calculate initial avg and stddev values
-	calculateBackgroundAvgStddev(I, avg, stddev, bg_radius);
-
-	// Remove white and black noise
-	if (white_dust_stddev > 0.)
-		removeDust(I, true, white_dust_stddev, avg, stddev);
-	if (black_dust_stddev > 0.)
-		removeDust(I, false, black_dust_stddev, avg, stddev);
-
-	// If some dust was removed: recalculate avg and stddev
 	if (white_dust_stddev > 0. || black_dust_stddev > 0.)
+	{
+		// Calculate initial avg and stddev values
 		calculateBackgroundAvgStddev(I, avg, stddev, bg_radius);
 
+		// Remove white and black noise
+		if (white_dust_stddev > 0.)
+			removeDust(I, true, white_dust_stddev, avg, stddev);
+		if (black_dust_stddev > 0.)
+			removeDust(I, false, black_dust_stddev, avg, stddev);
+	}
+
+	if (do_ramp)
+		subtractBackgroundRamp(I, bg_radius);
+
+	// Calculate avg and stddev (also redo if dust was removed!)
+	calculateBackgroundAvgStddev(I, avg, stddev, bg_radius);
 
 	if (stddev < 1e-10)
 	{
@@ -105,10 +109,10 @@ void normalise(Image<double> &I, int bg_radius, double white_dust_stddev, double
 	}
 }
 
-void calculateBackgroundAvgStddev(Image<double> &I, double &avg, double &stddev, int bg_radius)
+void calculateBackgroundAvgStddev(Image<DOUBLE> &I, DOUBLE &avg, DOUBLE &stddev, int bg_radius)
 {
 	int bg_radius2 = bg_radius * bg_radius;
-	double n = 0.;
+	DOUBLE n = 0.;
 	avg = 0.;
 	stddev = 0.;
 
@@ -128,18 +132,53 @@ void calculateBackgroundAvgStddev(Image<double> &I, double &avg, double &stddev,
 	{
 		if (k*k + i*i + j*j > bg_radius2)
 		{
-			double aux = A3D_ELEM(I(), k, i, j) - avg;
+			DOUBLE aux = A3D_ELEM(I(), k, i, j) - avg;
 			stddev += aux * aux;
 		}
 	}
 	stddev = sqrt(stddev/n);
 }
 
-void removeDust(Image<double> &I, bool is_white, double thresh, double avg, double stddev)
+
+void subtractBackgroundRamp(Image<DOUBLE> &I, int bg_radius)
+{
+
+	int bg_radius2 = bg_radius * bg_radius;
+	fit_point3D point;
+	std::vector<fit_point3D>  allpoints;
+    DOUBLE pA, pB, pC, avgbg, stddevbg, minbg, maxbg;
+
+    if (I().getDim() == 3)
+    	REPORT_ERROR("ERROR %% calculateBackgroundRamp is not implemented for 3D data!");
+
+    FOR_ALL_ELEMENTS_IN_ARRAY2D(I())
+	{
+		if (i*i + j*j > bg_radius2)
+		{
+            point.x = j;
+            point.y = i;
+            point.z = A2D_ELEM(I(), i, j);
+            point.w = 1.;
+            allpoints.push_back(point);
+		}
+	}
+
+    fitLeastSquaresPlane(allpoints, pA, pB, pC);
+
+    // Substract the plane from the image
+    FOR_ALL_ELEMENTS_IN_ARRAY2D(I())
+    {
+        A2D_ELEM(I(), i, j) -= pA * j + pB * i + pC;
+    }
+
+}
+
+
+void removeDust(Image<DOUBLE> &I, bool is_white, DOUBLE thresh, DOUBLE avg, DOUBLE stddev)
 {
 	FOR_ALL_ELEMENTS_IN_ARRAY3D(I())
 	{
-		double aux =  A3D_ELEM(I(), k, i, j);
+		DOUBLE aux =  A3D_ELEM(I(), k, i, j);
 		if (is_white && aux - avg > thresh * stddev)
 			A3D_ELEM(I(), k, i, j) = rnd_gaus(avg, stddev);
 		else if (!is_white && aux - avg < -thresh * stddev)
@@ -147,7 +186,7 @@ void removeDust(Image<double> &I, bool is_white, double thresh, double avg, doub
 	}
 }
 
-void invert_contrast(Image<double> &I)
+void invert_contrast(Image<DOUBLE> &I)
 {
 	FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(I())
 	{
@@ -155,33 +194,33 @@ void invert_contrast(Image<double> &I)
 	}
 }
 
-void rescale(Image<double> &I, int mysize)
+void rescale(Image<DOUBLE> &I, int mysize)
 {
 	int olddim = XSIZE(I());
 
 	resizeMap(I(), mysize);
 
 	// Also modify the scale in the MDmainheader (if present)
-	double oldscale, newscale;
+	DOUBLE oldscale, newscale;
     if (I.MDMainHeader.getValue(EMDL_IMAGE_SAMPLINGRATE_X, oldscale))
     {
-    	newscale = oldscale * (double)olddim / (double)mysize;
+    	newscale = oldscale * (DOUBLE)olddim / (DOUBLE)mysize;
     	I.MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_X, newscale);
     }
     if (I.MDMainHeader.getValue(EMDL_IMAGE_SAMPLINGRATE_Y, oldscale))
     {
-    	newscale = oldscale * (double)olddim / (double)mysize;
+    	newscale = oldscale * (DOUBLE)olddim / (DOUBLE)mysize;
     	I.MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Y, newscale);
     }
     if (I().getDim() == 3 && I.MDMainHeader.getValue(EMDL_IMAGE_SAMPLINGRATE_Z, oldscale) )
     {
-    	newscale = oldscale * (double)olddim / (double)mysize;
+    	newscale = oldscale * (DOUBLE)olddim / (DOUBLE)mysize;
     	I.MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Z, newscale);
     }
 
 }
 
-void rewindow(Image<double> &I, int mysize)
+void rewindow(Image<DOUBLE> &I, int mysize)
 {
 	// Check 2D or 3D dimensionality
 	if (I().getDim() == 2)
diff --git a/src/image.h b/src/image.h
index d0318e6..bad6618 100644
--- a/src/image.h
+++ b/src/image.h
@@ -151,7 +151,7 @@ public:
      * An empty image is created.
      *
      * @code
-     * Image<double> I;
+     * Image<DOUBLE> I;
      * @endcode
      */
     Image()
@@ -411,13 +411,13 @@ public:
             }
         case Double:
                 {
-                    if (typeid(T) == typeid(double))
+                    if (typeid(T) == typeid(DOUBLE))
                 {
                     memcpy(ptrDest, page, pageSize*sizeof(T));
                     }
                     else
                     {
-                        double * ptr = (double *) page;
+                        DOUBLE * ptr = (DOUBLE *) page;
                         for(int i=0; i<pageSize; i++)
                             ptrDest[i]=(T) ptr[i];
                     }
@@ -456,15 +456,15 @@ public:
             }
         case Double:
                 {
-                    if (typeid(T) == typeid(double))
+                    if (typeid(T) == typeid(DOUBLE))
                 {
                     memcpy(page, srcPtr, pageSize*sizeof(T));
                     }
                     else
                     {
-                        double * ptr = (double *) page;
+                        DOUBLE * ptr = (DOUBLE *) page;
                         for(int i=0; i<pageSize; i++)
-                            ptr[i] = (double)srcPtr[i];
+                            ptr[i] = (DOUBLE)srcPtr[i];
                     }
                 break;
             }
@@ -580,7 +580,7 @@ public:
             }
         case Double:
             {
-                if (typeid(T) == typeid(double))
+                if (typeid(T) == typeid(DOUBLE))
                     return 1;
                 else
                     return 0;
@@ -888,9 +888,9 @@ public:
     * std::cout << "sampling= " << samplingRateX() << std::endl;
     * @endcode
     */
-    double samplingRateX(const long int n = 0) const
+    DOUBLE samplingRateX(const long int n = 0) const
     {
-        double dummy = 1.;
+        DOUBLE dummy = 1.;
         MDMainHeader.getValue(EMDL_IMAGE_SAMPLINGRATE_X, dummy);
         return dummy;
     }
@@ -901,9 +901,9 @@ public:
     * std::cout << "sampling= " << samplingRateY() << std::endl;
     * @endcode
     */
-    double samplingRateY(const long int n = 0) const
+    DOUBLE samplingRateY(const long int n = 0) const
     {
-        double dummy = 1.;
+        DOUBLE dummy = 1.;
         MDMainHeader.getValue(EMDL_IMAGE_SAMPLINGRATE_Y, dummy);
         return dummy;
     }
@@ -920,7 +920,7 @@ public:
      */
     void setStatisticsInHeader()
     {
-    	double avg,stddev,minval,maxval;
+    	DOUBLE avg,stddev,minval,maxval;
     	data.computeStats(avg, stddev, minval, maxval);
     	MDMainHeader.setValue(EMDL_IMAGE_STATS_AVG, avg);
     	MDMainHeader.setValue(EMDL_IMAGE_STATS_STDDEV, stddev);
@@ -928,7 +928,7 @@ public:
     	MDMainHeader.setValue(EMDL_IMAGE_STATS_MAX, maxval);
     }
 
-    void setSamplingRateInHeader(double rate_x, double rate_y = -1., double rate_z = -1.)
+    void setSamplingRateInHeader(DOUBLE rate_x, DOUBLE rate_y = -1., DOUBLE rate_z = -1.)
     {
     	MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_X, rate_x);
     	if (rate_y < 0.)
@@ -1303,22 +1303,22 @@ private:
 
 // Some image-specific operations
 
-// For image normalization
-void normalise(Image<double> &I, int bg_radius, double white_dust_stddev, double black_dust_stddev);
-
-void calculateBackgroundAvgStddev(Image<double> &I, double &avg, double &stddev, int bg_radius);
+// For image normalisation
+void normalise(Image<DOUBLE> &I, int bg_radius, DOUBLE white_dust_stddev, DOUBLE black_dust_stddev, bool do_ramp);
+void calculateBackgroundAvgStddev(Image<DOUBLE> &I, DOUBLE &avg, DOUBLE &stddev, int bg_radius);
+void subtractBackgroundRamp(Image<DOUBLE> &I, int bg_radius);
 
 // For dust removal
-void removeDust(Image<double> &I, bool is_white, double thresh, double avg, double stddev);
+void removeDust(Image<DOUBLE> &I, bool is_white, DOUBLE thresh, DOUBLE avg, DOUBLE stddev);
 
 // for contrast inversion
-void invert_contrast(Image<double> &I);
+void invert_contrast(Image<DOUBLE> &I);
 
 // for image re-scaling
-void rescale(Image<double> &I, int mysize);
+void rescale(Image<DOUBLE> &I, int mysize);
 
 // for image re-windowing
-void rewindow(Image<double> &I, int mysize);
+void rewindow(Image<DOUBLE> &I, int mysize);
 
 
 
diff --git a/src/macros.h b/src/macros.h
index 0c35fc4..272eac9 100644
--- a/src/macros.h
+++ b/src/macros.h
@@ -60,6 +60,16 @@
 #define MAXFLOAT  1e30
 #endif
 
+#ifdef FLOAT_PRECISION
+#define DOUBLE float
+#define MY_MPI_DOUBLE MPI_FLOAT
+#else
+#define DOUBLE double
+#define MY_MPI_DOUBLE MPI_DOUBLE
+#endif
+
+
+
 //#define DEBUG
 //#define DEBUG_CHECKSIZES
 
@@ -79,9 +89,13 @@
 /** Equal accuracy
  *
  * In a comparison if two values are closer than this epsilon they are said to
- * be the same. Actually set to 1e-6
+ * be the same. Actually For double precision calculations set to 1e-6, for single-precision set to 1e-4 (finding symmetry subgroups will go wrong otherwise)
  */
+#ifdef FLOAT_PRECISION
+#define XMIPP_EQUAL_ACCURACY 1e-4
+#else
 #define XMIPP_EQUAL_ACCURACY 1e-6
+#endif
 //@}
 
 /// @name Numerical functions
@@ -177,8 +191,7 @@
  * a = CEIL(0.8); // a = 1
  * @endcode
  */
-#define CEIL(x) (((x) == (int)(x)) ? (int)(x):(((x) > 0) ? (int)((x) + 1) : \
-                 (int)(x)))
+#define CEIL(x) (((x) == (int)(x)) ? (int)(x):(((x) > 0) ? (int)((x) + 1) : (int)(x)))
 
 /** Round to next smaller integer
  *
@@ -192,8 +205,7 @@
  * a = FLOOR(0.8); // a = 0
  * @endcode
  */
-#define FLOOR(x) (((x) == (int)(x)) ? (int)(x):(((x) > 0) ? (int)(x) : \
-                  (int)((x) - 1)))
+#define FLOOR(x) (((x) == (int)(x)) ? (int)(x):(((x) > 0) ? (int)(x) : (int)((x) - 1)))
 
 /** Return the fractional part of a value
  *
@@ -224,10 +236,7 @@
  * output = ...  2 -2 -1  0  1  2 -2 -1  0  1  2 -2 -1  0  1  2 -2 ...
  * @endcode
  */
-#define intWRAP(x, x0, xF) (((x) >= (x0) && (x) <= (xF)) ? (x) : ((x) < (x0)) \
-                            ? ((x) - (int)(((x) - (x0) + 1) / ((xF) - (x0) + 1) - 1) * \
-                               ((xF) - (x0) + 1)) : ((x) - (int)(((x) - (xF) - 1) / ((xF) - (x0) + 1) \
-                                                                 + 1) * ((xF) - (x0) + 1)))
+#define intWRAP(x, x0, xF) (((x) >= (x0) && (x) <= (xF)) ? (x) : ((x) < (x0)) ? ((x) - (int)(((x) - (x0) + 1) / ((xF) - (x0) + 1) - 1) *  ((xF) - (x0) + 1)) : ((x) - (int)(((x) - (xF) - 1) / ((xF) - (x0) + 1) + 1) * ((xF) - (x0) + 1)))
 
 /** Wrapping for real numbers
  *
@@ -239,9 +248,7 @@
  * Corrected_angle = realWRAP(angle, 0, 2*PI);
  * @endcode
  */
-#define realWRAP(x, x0, xF) (((x) >= (x0) && (x) <= (xF)) ? (x) : ((x) < (x0)) \
-                             ? ((x) - (int)(((x) - (x0)) / ((xF) - (x0)) - 1) * ((xF) - (x0))) : \
-                             ((x) - (int)(((x) - (xF)) / ((xF) - (x0)) + 1) * ((xF) - (x0))))
+#define realWRAP(x, x0, xF) (((x) >= (x0) && (x) <= (xF)) ? (x) : ((x) < (x0))  ? ((x) - (int)(((x) - (x0)) / ((xF) - (x0)) - 1) * ((xF) - (x0))) : ((x) - (int)(((x) - (xF)) / ((xF) - (x0)) + 1) * ((xF) - (x0))))
 
 /** Degrees to radians
  *
@@ -299,8 +306,7 @@
  *
  * The sinc function is defined as sin(PI*x)/(PI*x).
  */
-#define SINC(x) (((x) < 0.0001 && (x) > -0.0001) ? 1 : sin(PI * (x)) \
-                 / (PI * (x)))
+#define SINC(x) (((x) < 0.0001 && (x) > -0.0001) ? 1 : sin(PI * (x)) / (PI * (x)))
 
 /** Returns next positive power_class of 2
  *
@@ -311,7 +317,7 @@
  * next_power = NEXT_POWER_OF_2(1000); // next_power = 1024
  * @endcode
  */
-#define NEXT_POWER_OF_2(x) pow(2, ceil(log((double) x) / log(2.0)-XMIPP_EQUAL_ACCURACY) )
+#define NEXT_POWER_OF_2(x) pow(2, ceil(log((DOUBLE) x) / log(2.0)-XMIPP_EQUAL_ACCURACY) )
 
 /** Linear interpolation
  *
@@ -320,20 +326,6 @@
  */
 #define LIN_INTERP(a, l, h) ((l) + ((h) - (l)) * (a))
 
-/** Cubic B-spline
- *
- */
-#define BSPLINE03(arg,result) {\
-	double x = ABS(arg); \
-	if (x < 1.0) \
-	    result=(x * x * (x - 2.0) * (1.0 / 2.0) + 2.0 / 3.0);\
-	else if (x < 2.0) { \
-	    x -= 2.0;\
-	    result=x*x*x*(-1.0 / 6.0); \
-	} else \
-            result=0;\
-    }
-
 /** XOR
  *
  * Logical Xor
@@ -344,31 +336,12 @@
 /// @name Miscellaneous
 //@{
 
-/** Speed up temporary variables
- *
- * The following variables are provided:
- *
- * @code
- * float spduptmp0, spduptmp1, spduptmp2;
- * int ispduptmp0, ispduptmp1, ispduptmp2, ispduptmp3, ispduptmp4, ispduptmp5;
- * @endcode
- */
-#define SPEED_UP_temps \
-    double spduptmp0, spduptmp1, spduptmp2, \
-    spduptmp3, spduptmp4, spduptmp5, \
-    spduptmp6, spduptmp7, spduptmp8; \
-    int   ispduptmp0, ispduptmp1, ispduptmp2, \
-    ispduptmp3, ispduptmp4, ispduptmp5;
-
 /** Swap two values
  *
  * It uses a temporal variable which must be of the same type as the two
  * parameters
  */
-#define SWAP(a, b, tmp) {\
-        tmp = a; \
-        a = b; \
-        b = tmp; }
+#define SWAP(a, b, tmp) { tmp = a; a = b; b = tmp; }
 
 /** Starting point for Xmipp volume/image
  *
diff --git a/src/manualpicker.cpp b/src/manualpicker.cpp
index 33ffa46..bc5b194 100644
--- a/src/manualpicker.cpp
+++ b/src/manualpicker.cpp
@@ -35,16 +35,17 @@ int last_ctf_viewed;
 
 
 bool   global_has_ctf;
-double global_angpix;
-double global_lowpass;
-double global_particle_diameter;
-double global_sigma_contrast;
-double global_black_val;
-double global_white_val;
-double global_micscale;
-double global_ctfscale;
-double global_blue_value;
-double global_red_value;
+DOUBLE global_angpix;
+DOUBLE global_lowpass;
+DOUBLE global_particle_diameter;
+DOUBLE global_sigma_contrast;
+DOUBLE global_black_val;
+DOUBLE global_white_val;
+DOUBLE global_micscale;
+DOUBLE global_ctfscale;
+DOUBLE global_ctfsigma;
+DOUBLE global_blue_value;
+DOUBLE global_red_value;
 int    global_total_count;
 FileName global_pickname;
 FileName global_fn_color;
@@ -60,7 +61,7 @@ void cb_viewmic(Fl_Widget* w, void* data)
 	int imic = *iptr;
 
 	// Update the count of the last one we picked...
-	if (last_pick_viewed >= 0 && last_pick_viewed < count_displays.size() && selected[last_pick_viewed])
+	if (last_pick_viewed > 0 && last_pick_viewed < count_displays.size())
 	{
 		MetaDataTable MDcoord;
 		FileName fn_coord = global_fn_mics[last_pick_viewed].withoutExtension() + "_" + global_pickname + ".star";
@@ -118,7 +119,6 @@ void cb_viewmic(Fl_Widget* w, void* data)
 		{
 			viewmic_buttons[i]->color(GUI_BUTTON_COLOR, GUI_BUTTON_COLOR);
 		}
-		viewmic_buttons[i]->redraw();
 	}
 
 
@@ -134,6 +134,7 @@ void cb_viewctf(Fl_Widget* w, void* data)
 	std::string command;
 	command =  "relion_display --i " + global_fn_ctfs[imic];
 	command += " --scale " + floatToString(global_ctfscale);
+	command += " --sigma_contrast " + floatToString(global_ctfsigma);
 	command += " &";
 	std::cerr << command << std::endl;
 	system(command.c_str());
@@ -149,7 +150,6 @@ void cb_viewctf(Fl_Widget* w, void* data)
 		{
 			viewctf_buttons[i]->color(GUI_BUTTON_COLOR, GUI_BUTTON_COLOR);
 		}
-		viewctf_buttons[i]->redraw();
 	}
 
 
@@ -451,6 +451,7 @@ void ManualPicker::read(int argc, char **argv)
 	global_lowpass = textToFloat(parser.getOption("--lowpass", "Lowpass filter in Angstroms for the micrograph (0 for no filtering)","0"));
 
 	global_ctfscale = textToFloat(parser.getOption("--ctf_scale", "Relative scale for the CTF-image display", "1"));
+	global_ctfsigma = textToFloat(parser.getOption("--ctf_sigma_contrast", "Sigma-contrast for the CTF-image display", "3"));
 
 	// coloring
 	global_fn_color = parser.getOption("--color_star", "STAR file with a column for red-blue coloring (a subset of) the particles", "");
@@ -474,7 +475,7 @@ void ManualPicker::initialise()
 
 	if (global_micscale < 1.)
 	{
-		double new_nyquist = global_angpix * 2. / global_micscale;
+		DOUBLE new_nyquist = global_angpix * 2. / global_micscale;
 		if (new_nyquist > global_lowpass)
 			global_lowpass = new_nyquist;
 		std::cout << " Set low-pass filter to " << global_lowpass << " due to downscaling of " << global_micscale << std::endl;
diff --git a/src/mask.cpp b/src/mask.cpp
index 7ffd19d..acf60a4 100644
--- a/src/mask.cpp
+++ b/src/mask.cpp
@@ -21,13 +21,13 @@
 
 // Mask out corners outside sphere (replace by average value)
 // Apply a soft mask (raised cosine with cosine_width pixels width)
-void softMaskOutsideMap(MultidimArray<double> &vol, double radius, double cosine_width, MultidimArray<double> *Mnoise)
+void softMaskOutsideMap(MultidimArray<DOUBLE> &vol, DOUBLE radius, DOUBLE cosine_width, MultidimArray<DOUBLE> *Mnoise)
 {
 
 	vol.setXmippOrigin();
-	double r, radius_p, raisedcos, sum_bg = 0., sum = 0.;
+	DOUBLE r, radius_p, raisedcos, sum_bg = 0., sum = 0.;
 	if (radius < 0)
-		radius = (double)XSIZE(vol)/2.;
+		radius = (DOUBLE)XSIZE(vol)/2.;
 	radius_p = radius + cosine_width;
 
 
@@ -36,7 +36,7 @@ void softMaskOutsideMap(MultidimArray<double> &vol, double radius, double cosine
 		// Calculate average background value
 		FOR_ALL_ELEMENTS_IN_ARRAY3D(vol)
 		{
-			r = sqrt((double)(k*k + i*i + j*j));
+			r = sqrt((DOUBLE)(k*k + i*i + j*j));
 			if (r < radius)
 				continue;
 			else if (r > radius_p)
@@ -57,7 +57,7 @@ void softMaskOutsideMap(MultidimArray<double> &vol, double radius, double cosine
 	// Apply noisy or average background value
 	FOR_ALL_ELEMENTS_IN_ARRAY3D(vol)
 	{
-		r = sqrt((double)(k*k + i*i + j*j));
+		r = sqrt((DOUBLE)(k*k + i*i + j*j));
 		if (r < radius)
 		{
 			continue;
@@ -69,14 +69,14 @@ void softMaskOutsideMap(MultidimArray<double> &vol, double radius, double cosine
 		else
 		{
 			raisedcos = 0.5 + 0.5 * cos(PI * (radius_p - r) / cosine_width );
-			double add = (Mnoise == NULL) ?  sum_bg : A3D_ELEM(*Mnoise, k, i, j);
+			DOUBLE add = (Mnoise == NULL) ?  sum_bg : A3D_ELEM(*Mnoise, k, i, j);
 			A3D_ELEM(vol, k, i, j) = (1 - raisedcos) * A3D_ELEM(vol, k, i, j) + raisedcos * add;
 		}
 	}
 
 }
 
-void softMaskOutsideMap(MultidimArray<double> &vol, MultidimArray<double> &msk, bool invert_mask)
+void softMaskOutsideMap(MultidimArray<DOUBLE> &vol, MultidimArray<DOUBLE> &msk, bool invert_mask)
 {
 
 	if (msk.computeMax() > 1. || msk.computeMin() < 0.)
@@ -88,9 +88,9 @@ void softMaskOutsideMap(MultidimArray<double> &vol, MultidimArray<double> &msk,
 		REPORT_ERROR("ERROR: Solvent mask does not have the same size as the reference vol.");
 
 	// Replace solvent by the average value in the solvent region
-	double sum = 0.;
-	double sum_bg = 0.;
-	double solv;
+	DOUBLE sum = 0.;
+	DOUBLE sum_bg = 0.;
+	DOUBLE solv;
 	FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(msk)
 	{
 		solv = (invert_mask) ? DIRECT_A3D_ELEM(msk, k, i, j) : 1. - DIRECT_A3D_ELEM(msk, k, i, j);
@@ -108,11 +108,11 @@ void softMaskOutsideMap(MultidimArray<double> &vol, MultidimArray<double> &msk,
 
 }
 
-void autoMask(MultidimArray<double> &img_in, MultidimArray<double> &msk_out,
-		double ini_mask_density_threshold, double extend_ini_mask, double width_soft_mask_edge, bool verb)
+void autoMask(MultidimArray<DOUBLE> &img_in, MultidimArray<DOUBLE> &msk_out,
+		DOUBLE ini_mask_density_threshold, DOUBLE extend_ini_mask, DOUBLE width_soft_mask_edge, bool verb)
 
 {
-	MultidimArray<double> msk_cp;
+	MultidimArray<DOUBLE> msk_cp;
 	int barstep, update_bar, totalbar;
 
 	// Resize output mask
@@ -145,7 +145,7 @@ void autoMask(MultidimArray<double> &img_in, MultidimArray<double> &msk_out,
 		}
 
 		int extend_size = ABS(CEIL(extend_ini_mask));
-		double extend_ini_mask2 = extend_ini_mask * extend_ini_mask;
+		DOUBLE extend_ini_mask2 = extend_ini_mask * extend_ini_mask;
 		msk_cp = msk_out;
 		if (extend_ini_mask > 0.)
 		{
@@ -168,7 +168,7 @@ void autoMask(MultidimArray<double> &img_in, MultidimArray<double> &msk_out,
 									// only check distance if neighbouring Im() is one
 									if (A3D_ELEM(msk_cp, kp, ip, jp) > 0.999)
 									{
-										double r2 = (double)( (kp-k)*(kp-k) + (ip-i)*(ip-i)+ (jp-j)*(jp-j) );
+										DOUBLE r2 = (DOUBLE)( (kp-k)*(kp-k) + (ip-i)*(ip-i)+ (jp-j)*(jp-j) );
 										// Set original voxel to 1 if a neghouring with Im()=1 is within distance extend_ini_mask
 										if (r2 < extend_ini_mask2)
 										{
@@ -217,7 +217,7 @@ void autoMask(MultidimArray<double> &img_in, MultidimArray<double> &msk_out,
 									// only check distance if neighbouring Im() is one
 									if (A3D_ELEM(msk_cp, kp, ip, jp) < 0.001)
 									{
-										double r2 = (double)( (kp-k)*(kp-k) + (ip-i)*(ip-i)+ (jp-j)*(jp-j) );
+										DOUBLE r2 = (DOUBLE)( (kp-k)*(kp-k) + (ip-i)*(ip-i)+ (jp-j)*(jp-j) );
 										// Set original voxel to 1 if a neghouring with Im()=1 is within distance extend_ini_mask
 										if (r2 < extend_ini_mask2)
 										{
@@ -264,13 +264,13 @@ void autoMask(MultidimArray<double> &img_in, MultidimArray<double> &msk_out,
 
 		msk_cp = msk_out;
 		int extend_size = CEIL(width_soft_mask_edge);
-		double width_soft_mask_edge2 = width_soft_mask_edge * width_soft_mask_edge;
+		DOUBLE width_soft_mask_edge2 = width_soft_mask_edge * width_soft_mask_edge;
 		FOR_ALL_ELEMENTS_IN_ARRAY3D(msk_cp)
 		{
 			// only extend zero values to values between 0 and 1.
 			if (A3D_ELEM(msk_cp, k, i, j) < 0.001)
 			{
-				double min_r2 = 9999.;
+				DOUBLE min_r2 = 9999.;
 				for (long int kp = k - extend_size; kp <= k + extend_size; kp++)
 				{
 					for (long int ip = i - extend_size; ip <= i + extend_size; ip++)
@@ -284,7 +284,7 @@ void autoMask(MultidimArray<double> &img_in, MultidimArray<double> &msk_out,
 								// only update distance to a neighbouring msk_cp is one
 								if (A3D_ELEM(msk_cp, kp, ip, jp) > 0.999)
 								{
-									double r2 = (double)( (kp-k)*(kp-k) + (ip-i)*(ip-i)+ (jp-j)*(jp-j) );
+									DOUBLE r2 = (DOUBLE)( (kp-k)*(kp-k) + (ip-i)*(ip-i)+ (jp-j)*(jp-j) );
 									// Set original voxel to 1 if a neghouring with Im()=1 is within distance extend_ini_mask
 									if (r2 < min_r2)
 										min_r2 = r2;
@@ -314,13 +314,13 @@ void autoMask(MultidimArray<double> &img_in, MultidimArray<double> &msk_out,
 	}
 
 }
-void raisedCosineMask(MultidimArray<double> &mask, double radius, double radius_p, int x, int y, int z)
+void raisedCosineMask(MultidimArray<DOUBLE> &mask, DOUBLE radius, DOUBLE radius_p, int x, int y, int z)
 {
 	mask.setXmippOrigin();
 	FOR_ALL_ELEMENTS_IN_ARRAY3D(mask)
 	{
 		// calculate distance from the origin
-		double d = sqrt((double)((z-k)*(z-k) + (y-i)*(y-i) + (x-j)*(x-j)));
+		DOUBLE d = sqrt((DOUBLE)((z-k)*(z-k) + (y-i)*(y-i) + (x-j)*(x-j)));
 		if (d > radius_p)
 			A3D_ELEM(mask, k, i, j) = 0.;
 		else if (d < radius)
diff --git a/src/mask.h b/src/mask.h
index 7f63cd3..8b76fbd 100644
--- a/src/mask.h
+++ b/src/mask.h
@@ -27,21 +27,21 @@
 
 // Mask out corners outside sphere (replace by average value)
 // Apply a soft mask (raised cosine with cosine_width pixels width)
-void softMaskOutsideMap(MultidimArray<double> &vol, double radius = -1., double cosine_width = 3, MultidimArray<double> *Mnoise = NULL);
+void softMaskOutsideMap(MultidimArray<DOUBLE> &vol, DOUBLE radius = -1., DOUBLE cosine_width = 3, MultidimArray<DOUBLE> *Mnoise = NULL);
 
 // Apply a soft mask and set density outside the mask at the average value of those pixels in the original map
-void softMaskOutsideMap(MultidimArray<double> &vol, MultidimArray<double> &msk, bool invert_mask = false);
+void softMaskOutsideMap(MultidimArray<DOUBLE> &vol, MultidimArray<DOUBLE> &msk, bool invert_mask = false);
 
 // Make an automated mask, based on:
 // 1. initial binarization (based on ini_mask_density_threshold)
 // 2. Growing extend_ini_mask in all directions
 // 3. Putting a raised-cosine edge on the mask with width width_soft_mask_edge
 // If verb, then output description of steps and progress bars
-void autoMask(MultidimArray<double> &img_in, MultidimArray<double> &msk_out,
-		double  ini_mask_density_threshold, double extend_ini_mask, double width_soft_mask_edge, bool verb = false);
+void autoMask(MultidimArray<DOUBLE> &img_in, MultidimArray<DOUBLE> &msk_out,
+		DOUBLE  ini_mask_density_threshold, DOUBLE extend_ini_mask, DOUBLE width_soft_mask_edge, bool verb = false);
 
 // Fills mask with a soft-edge circular mask (soft-edge in between radius and radius_p), centred at (x, y, z)
-void raisedCosineMask(MultidimArray<double> &mask, double radius, double radius_p, int x, int y, int z = 0);
+void raisedCosineMask(MultidimArray<DOUBLE> &mask, DOUBLE radius, DOUBLE radius_p, int x, int y, int z = 0);
 
 
 #endif /* MASK_H_ */
diff --git a/src/matrix1d.cpp b/src/matrix1d.cpp
index c3ba17f..7fbdf97 100644
--- a/src/matrix1d.cpp
+++ b/src/matrix1d.cpp
@@ -44,23 +44,31 @@
 
 #include "src/matrix1d.h"
 
-Matrix1D<double> vectorR2(double x, double y)
+Matrix1D<DOUBLE> vectorR2(DOUBLE x, DOUBLE y)
 {
-    Matrix1D<double> result(2);
+    Matrix1D<DOUBLE> result(2);
     result( 0) = x;
     result( 1) = y;
     return result;
 }
 
-Matrix1D<double> vectorR3(double x, double y, double z)
+Matrix1D<DOUBLE> vectorR3(DOUBLE x, DOUBLE y, DOUBLE z)
 {
-    Matrix1D<double> result(3);
+    Matrix1D<DOUBLE> result(3);
     result( 0) = x;
     result( 1) = y;
     result( 2) = z;
     return result;
 }
 
+// This function only makes sense after all code has been modified with 'sed' to allow single-precision runs
+#ifdef FLOAT_PRECISION
+Matrix1D<float> vectorR3(double xx, double yy, double zz)
+{
+	return vectorR3((float)xx, (float)yy, (float)zz);
+}
+#endif
+
 Matrix1D<int> vectorR3(int x, int y, int z)
 {
     Matrix1D<int> result(3);
diff --git a/src/matrix1d.h b/src/matrix1d.h
index 33e6ed6..eb9d989 100644
--- a/src/matrix1d.h
+++ b/src/matrix1d.h
@@ -123,7 +123,7 @@ template <typename T> class Matrix2D;
  * the vector is (x, y) in R2.
  *
  * @code
- * MultidimArray< double > v(2);
+ * MultidimArray< DOUBLE > v(2);
  * VECTOR_R2(v, 1, 2);
  * @endcode
  */
@@ -135,7 +135,7 @@ template <typename T> class Matrix2D;
  * the vector is (x, y, z) in R3.
  *
  * @code
- * MultidimArray< double > v(3);
+ * MultidimArray< DOUBLE > v(3);
  * VECTOR_R2(v, 1, 2, 1);
  * @endcode
  */
@@ -144,7 +144,7 @@ template <typename T> class Matrix2D;
 
 /** Adding two R2 vectors (a=b+c)
  * @code
- * MultidimArray< double > a(2), b(2), c(2);
+ * MultidimArray< DOUBLE > a(2), b(2), c(2);
  * ...;
  * V2_PLUS_V2(a, b, c);
  * @endcode
@@ -155,7 +155,7 @@ template <typename T> class Matrix2D;
 
 /** Substracting two R2 vectors (a=b-c)
  * @code
- * MultidimArray< double > a(2), b(2), c(2);
+ * MultidimArray< DOUBLE > a(2), b(2), c(2);
  * ...;
  * V2_MINUS_V2(a, b, c);
  * @endcode
@@ -166,13 +166,13 @@ template <typename T> class Matrix2D;
 
 /** Adding/substracting a constant to a R2 vector (a=b-k).
  * @code
- * MultidimArray< double > a(2), b(2);
- * double k;
+ * MultidimArray< DOUBLE > a(2), b(2);
+ * DOUBLE k;
  * ...;
  * V2_PLUS_CT(a, b, k);
  *
- * MultidimArray< double > a(2), b(2);
- * double k;
+ * MultidimArray< DOUBLE > a(2), b(2);
+ * DOUBLE k;
  * ...;
  * V2_PLUS_CT(a, b, -k);
  * @endcode
@@ -183,13 +183,13 @@ template <typename T> class Matrix2D;
 
 /** Multiplying/dividing by a constant a R2 vector (a=b*k)
  * @code
- * MultidimArray< double > a(2), b(2);
- * double k;
+ * MultidimArray< DOUBLE > a(2), b(2);
+ * DOUBLE k;
  * ...;
  * V2_BY_CT(a, b, k);
  *
- * MultidimArray< double > a(2), b(2);
- * double k;
+ * MultidimArray< DOUBLE > a(2), b(2);
+ * DOUBLE k;
  * ...;
  * V2_BY_CT(a, b, 1/k);
  * @endcode
@@ -200,7 +200,7 @@ template <typename T> class Matrix2D;
 
 /** Adding two R3 vectors (a=b+c)
  * @code
- * MultidimArray< double > a(3), b(3), c(3);
+ * MultidimArray< DOUBLE > a(3), b(3), c(3);
  * ...;
  * V3_PLUS_V3(a, b, c);
  * @endcode
@@ -212,7 +212,7 @@ template <typename T> class Matrix2D;
 
 /** Substracting two R3 vectors (a=b-c)
  * @code
- * MultidimArray< double > a(3), b(3), c(3);
+ * MultidimArray< DOUBLE > a(3), b(3), c(3);
  * ...;
  * V3_MINUS_V3(a, b, c);
  * @endcode
@@ -224,13 +224,13 @@ template <typename T> class Matrix2D;
 
 /** Adding/substracting a constant to a R3 vector (a=b-k)
  * @code
- * MultidimArray< double > a(3), b(3);
- * double k;
+ * MultidimArray< DOUBLE > a(3), b(3);
+ * DOUBLE k;
  * ...;
  * V3_PLUS_CT(a, b, k);
  *
- * MultidimArray< double > a(3), b(3);
- * double k;
+ * MultidimArray< DOUBLE > a(3), b(3);
+ * DOUBLE k;
  * ...;
  * V3_PLUS_CT(a, b, -k);
  * @endcode
@@ -242,13 +242,13 @@ template <typename T> class Matrix2D;
 
 /** Multiplying/dividing by a constant a R3 vector (a=b*k)
  * @code
- * MultidimArray< double > a(3), b(3);
- * double k;
+ * MultidimArray< DOUBLE > a(3), b(3);
+ * DOUBLE k;
  * ...;
  * V3_BY_CT(a, b, k);
  *
- * MultidimArray< double > a(3), b(3);
- * double k;
+ * MultidimArray< DOUBLE > a(3), b(3);
+ * DOUBLE k;
  * ...;
  * V3_BY_CT(a, b, 1/k);
  * @endcode
@@ -289,8 +289,8 @@ public:
      * vector (by default), or a row one.
      *
      * @code
-     * Matrix1D< double > v1;
-     * Matrix1D< double > v1(true);
+     * Matrix1D< DOUBLE > v1;
+     * Matrix1D< DOUBLE > v1(true);
      * // both are examples of empty column vectors
      *
      * Matrix1D< int > v1(false);
@@ -311,8 +311,8 @@ public:
      * one.
      *
      * @code
-     * Matrix1D< double > v1(6);
-     * Matrix1D< double > v1(6, 'y');
+     * Matrix1D< DOUBLE > v1(6);
+     * Matrix1D< DOUBLE > v1(6, 'y');
      * // both are examples of column vectors of dimensions 6
      *
      * Matrix1D< int > v1('n');
@@ -332,7 +332,7 @@ public:
      * different memory assignment.
      *
      * @code
-     * Matrix1D< double > v2(v1);
+     * Matrix1D< DOUBLE > v2(v1);
      * @endcode
      */
     Matrix1D(const Matrix1D<T>& v)
@@ -567,7 +567,7 @@ public:
     /** Same value in all components.
      *
      * The constant must be of a type compatible with the array type, ie,
-     * you cannot  assign a double to an integer array without a casting.
+     * you cannot  assign a DOUBLE to an integer array without a casting.
      * It is not an error if the array is empty, then nothing is done.
      *
      * @code
@@ -1009,18 +1009,18 @@ public:
      * This function returns the sum of all internal values.
      *
      * @code
-     * double sum = m.sum();
+     * DOUBLE sum = m.sum();
      * @endcode
      */
-    double sum(bool average=false) const
+    DOUBLE sum(bool average=false) const
     {
-        double sum = 0;
+        DOUBLE sum = 0;
 		for (int j = 0; j < vdim; j++)
 		{
 			sum += vdata[j];
 		}
 		if (average)
-			return sum/(double)vdim;
+			return sum/(DOUBLE)vdim;
 		else
 			return sum;
     }
@@ -1031,12 +1031,12 @@ public:
      * power_class.
      *
      * @code
-     * double sum2 = m.sum2();
+     * DOUBLE sum2 = m.sum2();
      * @endcode
      */
-    double sum2() const
+    DOUBLE sum2() const
     {
-        double sum = 0;
+        DOUBLE sum = 0;
 		for (int j = 0; j < vdim; j++)
 		{
 			sum += vdata[j] * vdata[j];
@@ -1050,10 +1050,10 @@ public:
      * components. Euclidean norm of the vector.
      *
      * @code
-     * double mod = v.module();
+     * DOUBLE mod = v.module();
      * @endcode
      */
-    double module() const
+    DOUBLE module() const
     {
         return sqrt(sum2());
     }
@@ -1063,16 +1063,16 @@ public:
      * Supposing this vector is in R2 this function returns the angle of this
      * vector with X axis, ie, atan2(YY(v), XX(v))
      */
-    double angle()
+    DOUBLE angle()
     {
-        return atan2((double) YY(*this), (double) XX(*this));
+        return atan2((DOUBLE) YY(*this), (DOUBLE) XX(*this));
     }
 
     /** Normalize this vector, store the result here
      */
     void selfNormalize()
     {
-        double m = module();
+        DOUBLE m = module();
         if (ABS(m) > XMIPP_EQUAL_ACCURACY)
         {
             T im=(T) (1.0/m);
@@ -1100,9 +1100,9 @@ public:
      * because the numerical method is not able to correctly estimate the
      * derivative there.
      */
-    void numericalDerivative(Matrix1D<double> &result)
+    void numericalDerivative(Matrix1D<DOUBLE> &result)
     {
-         const double i12=1.0/12.0;
+         const DOUBLE i12=1.0/12.0;
          result.initZeros(*this);
          for (int i=STARTINGX(*this)+2; i<=FINISHINGX(*this)-2; i++)
         	 result(i)=i12*(-(*this)(i+2)+8*(*this)(i+1)
@@ -1117,7 +1117,7 @@ public:
         else
             ostrm << std::endl;
 
-        double max_val = ABS(v.vdata[0]);
+        DOUBLE max_val = ABS(v.vdata[0]);
 
         for (int j = 0; j < v.vdim; j++)
         {
@@ -1128,7 +1128,7 @@ public:
 
         for (int j = 0; j < v.vdim; j++)
         {
-       	 ostrm << floatToString((double) v.vdata[j], 10, prec)
+       	 ostrm << floatToString((DOUBLE) v.vdata[j], 10, prec)
        	 << std::endl;
         }
         return ostrm;
@@ -1145,19 +1145,24 @@ public:
   * After this function the vector is (x,y) in R2.
   *
   * @code
-  * Matrix1D< double > v = vectorR2(1, 2);
+  * Matrix1D< DOUBLE > v = vectorR2(1, 2);
   * @endcode
   */
- Matrix1D< double > vectorR2(double x, double y);
+ Matrix1D< DOUBLE > vectorR2(DOUBLE x, DOUBLE y);
 
  /** Creates vector in R3.
   * After this function the vector is (x,y,z) in R3.
   *
   * @code
-  * Matrix1D< double > v = vectorR2(1, 2, 1);
+  * Matrix1D< DOUBLE > v = vectorR2(1, 2, 1);
   * @endcode
   */
- Matrix1D< double > vectorR3(double x, double y, double z);
+ Matrix1D< DOUBLE > vectorR3(DOUBLE x, DOUBLE y, DOUBLE z);
+
+ // This function is only needed for single-precision compilation
+#ifdef FLOAT_PRECISION
+Matrix1D< float > vectorR3(double xx, double yy, double zz);
+#endif
 
  /** Creates an integer vector in Z3.
   */
@@ -1173,7 +1178,7 @@ public:
   * V1y*V2y + V1z*V2z.
   *
   * @code
-  * Matrix1D< double > v1(1000);
+  * Matrix1D< DOUBLE > v1(1000);
   * v1.init_random(0, 10, "gaussian");
   * std::cout << "The power_class of this vector should be 100 and is " <<
   *     dotProduct(v1, v1) << std::endl;
@@ -1264,7 +1269,7 @@ public:
  }
 
 /** Conversion from one type to another.
-  * If we have an integer array and we need a double one, we can use this
+  * If we have an integer array and we need a DOUBLE one, we can use this
   * function. The conversion is done through a type casting of each element
   * If n >= 0, only the nth volumes will be converted, otherwise all NSIZE volumes
   */
diff --git a/src/matrix2d.cpp b/src/matrix2d.cpp
index c189743..e799b9a 100644
--- a/src/matrix2d.cpp
+++ b/src/matrix2d.cpp
@@ -22,8 +22,8 @@
 #include "src/matrix2d.h"
 
 /* Interface to numerical recipes: svbksb ---------------------------------- */
-void svbksb(Matrix2D<double> &u, Matrix1D<double> &w, Matrix2D<double> &v,
-            Matrix1D<double> &b, Matrix1D<double> &x)
+void svbksb(Matrix2D<DOUBLE> &u, Matrix1D<DOUBLE> &w, Matrix2D<DOUBLE> &v,
+            Matrix1D<DOUBLE> &b, Matrix1D<DOUBLE> &x)
 {
     // Call to the numerical recipes routine. Results will be stored in X
     svbksb(u.adaptForNumericalRecipes2(),
diff --git a/src/matrix2d.h b/src/matrix2d.h
index 448712d..2b1e2da 100644
--- a/src/matrix2d.h
+++ b/src/matrix2d.h
@@ -97,69 +97,6 @@
  */
 #define MAT_YSIZE(m) ((m).mdimy)
 
-/** Matrix (2x2) by vector (2x1) (a=M*b)
- *
- * You must "load" the temporary variables, and create the result vector with
- * the appropiate size. You can reuse the vector b to store the results (that
- * is, M2x2_BY_V2x1(b, M, b);, is allowed).
- *
- * @code
- * double example
- * {
- *     SPEED_UP_temps;
- *
- *     Matrix1D< double > a(2), b(2);
- *     Matrix2D< double > M(2, 2);
- *
- *     M.init_random(0, 1);
- *     b.init_random(0, 1);
- *
- *     M2x2_BY_V2x1(a, M, b);
- *
- *     return a.sum();
- * }
- * @endcode
- * 	TODO: remove this from the code.... DONT USE IT ANY FURTHER....
- */
-#define M2x2_BY_V2x1(a, M, b) { \
-        spduptmp0 = MAT_ELEM(M, 0, 0) * XX(b) + MAT_ELEM(M, 0, 1) * YY(b); \
-        spduptmp1 = MAT_ELEM(M, 1, 0) * XX(b) + MAT_ELEM(M, 1, 1) * YY(b); \
-        XX(a) = spduptmp0; \
-        YY(a) = spduptmp1; }
-
-/** Matrix (3x3) by vector (3x1) (a=M*b)
- *
- * You must "load" the temporary variables, and create the result vector with
- * the appropiate size. You can reuse the vector b to store the results (that
- * is, M3x3_BY_V3x1(b, M, b);, is allowed).
- *
- * @code
- * double example
- * {
- *     SPEED_UP_temps;
- *
- *     Matrix1D< double > a(3), b(3);
- *     Matrix2D< double > M(3, 3);
- *
- *     M.init_random(0, 1);
- *     b.init_random(0, 1);
- *     M3x3_BY_V3x1(a, M, b);
- *
- *     return a.sum();
- * }
- * @endcode
- * 	TODO: remove this from the code.... DONT USE IT ANY FURTHER....
- */
-#define M3x3_BY_V3x1(a, M, b) { \
-        spduptmp0 = MAT_ELEM(M, 0, 0) * XX(b) + MAT_ELEM(M, 0, 1) * YY(b) + MAT_ELEM(M, 0, 2) \
-                    * ZZ(b); \
-        spduptmp1 = MAT_ELEM(M, 1, 0) * XX(b) + MAT_ELEM(M, 1, 1) * YY(b) + MAT_ELEM(M, 1, 2) \
-                    * ZZ(b); \
-        spduptmp2 = MAT_ELEM(M, 2, 0) * XX(b) + MAT_ELEM(M, 2, 1) * YY(b) + MAT_ELEM(M, 2, 2) \
-                    * ZZ(b); \
-        XX(a) = spduptmp0; YY(a) = spduptmp1; ZZ(a) = spduptmp2; }
-
-
 // Forward declarations
 template<typename T>
 class Matrix1D;
@@ -174,21 +111,21 @@ void lubksb(const Matrix2D<T>& LU, Matrix1D< int >& indx, Matrix1D<T>& b);
 
 template<typename T>
 void svdcmp(const Matrix2D< T >& a,
-            Matrix2D< double >& u,
-            Matrix1D< double >& w,
-            Matrix2D< double >& v);
+            Matrix2D< DOUBLE >& u,
+            Matrix1D< DOUBLE >& w,
+            Matrix2D< DOUBLE >& v);
 
-void svbksb(Matrix2D< double >& u,
-            Matrix1D< double >& w,
-            Matrix2D< double >& v,
-            Matrix1D< double >& b,
-            Matrix1D< double >& x);
+void svbksb(Matrix2D< DOUBLE >& u,
+            Matrix1D< DOUBLE >& w,
+            Matrix2D< DOUBLE >& v,
+            Matrix1D< DOUBLE >& b,
+            Matrix1D< DOUBLE >& x);
 
 template<typename T>
 void solve(const Matrix2D<T>& A,
 		   const Matrix1D<T>& b,
-           Matrix1D< double >& result,
-           double tolerance);
+           Matrix1D< DOUBLE >& result,
+           DOUBLE tolerance);
 
 /** Matrix2D class */
 template<typename T>
@@ -435,7 +372,7 @@ public:
     /** Same value in all components.
      *
      * The constant must be of a type compatible with the array type, ie,
-     * you cannot  assign a double to an integer array without a casting.
+     * you cannot  assign a DOUBLE to an integer array without a casting.
      * It is not an error if the array is empty, then nothing is done.
      *
      * @code
@@ -512,8 +449,7 @@ public:
      */
     void initIdentity(int dim)
     {
-        if (mdimx!=dim || mdimy!=dim)
-            resize(dim, dim);
+        initZeros(dim, dim);
         for (int i = 0; i < dim; i++)
             MAT_ELEM(*this,i,i) = 1;
     }
@@ -711,7 +647,7 @@ public:
      * than the argument and the same values (within accuracy).
      */
     bool equal(const Matrix2D<T>& op,
-               double accuracy = XMIPP_EQUAL_ACCURACY) const
+               DOUBLE accuracy = XMIPP_EQUAL_ACCURACY) const
     {
         if (!sameShape(op))
             return false;
@@ -728,7 +664,7 @@ public:
     /** Set very small values (ABS(val)< accuracy) equal to zero
       *
       */
-    void setSmallValuesToZero(double accuracy = XMIPP_EQUAL_ACCURACY)
+    void setSmallValuesToZero(DOUBLE accuracy = XMIPP_EQUAL_ACCURACY)
     {
         for (int i = 0; i < mdimy; i++)
              for (int j = 0; j < mdimx; j++)
@@ -774,7 +710,7 @@ public:
     *
     * This function must be used only as a preparation for routines which need
     * that the first physical index is 1 and not 0 as it usually is in C. New
-    * memory is needed to hold the new double pointer array.
+    * memory is needed to hold the new DOUBLE pointer array.
     */
     T** adaptForNumericalRecipes() const
     {
@@ -848,14 +784,14 @@ public:
         else
         {
             ostrm << std::endl;
-            double max_val = v.computeMax();
+            DOUBLE max_val = v.computeMax();
             int prec = bestPrecision(max_val, 10);
 
             for (int i = 0; i < v.Ydim(); i++)
             {
                 for (int j = 0; j < v.Xdim(); j++)
                 {
-                    ostrm << std::setw(13) << floatToString((double) v(i, j), 10, prec) << ' ';
+                    ostrm << std::setw(13) << floatToString((DOUBLE) v(i, j), 10, prec) << ' ';
                 }
                 ostrm << std::endl;
             }
@@ -871,7 +807,7 @@ public:
      * according to the shape.
      *
      * @code
-     * Matrix2D< double > m = fromVector(v);
+     * Matrix2D< DOUBLE > m = fromVector(v);
      * @endcode
      */
     void fromVector(const Matrix1D<T>& op1)
@@ -909,7 +845,7 @@ public:
      * matrix.
      *
      * @code
-     * Matrix1D< double > v;
+     * Matrix1D< DOUBLE > v;
      * m.toVector(v);
      * @endcode
      */
@@ -973,7 +909,7 @@ public:
      * logical not physical.
      *
      * @code
-     * std::vector< double > v;
+     * std::vector< DOUBLE > v;
      * m.getRow(-2, v);
      * @endcode
      */
@@ -1002,7 +938,7 @@ public:
      * choosen column.
      *
      * @code
-     * std::vector< double > v;
+     * std::vector< DOUBLE > v;
      * m.getCol(-1, v);
      * @endcode
      */
@@ -1083,7 +1019,7 @@ public:
      * An exception is thrown if the matrix is not squared or it is empty.
      *
      * @code
-     * double det = m.det();
+     * DOUBLE det = m.det();
      * @endcode
      */
     T det() const
@@ -1145,7 +1081,7 @@ public:
      * pseudoinverse is returned.
      *
      * @code
-     * Matrix2D< double > m1_inv;
+     * Matrix2D< DOUBLE > m1_inv;
      * m1.inv(m1_inv);
      * @endcode
      */
@@ -1170,7 +1106,7 @@ public:
         	MAT_ELEM(result, 2, 0) =   MAT_ELEM((*this), 2, 1)*MAT_ELEM((*this), 1, 0)-MAT_ELEM((*this), 2, 0)*MAT_ELEM((*this), 1, 1);
         	MAT_ELEM(result, 2, 1) = -(MAT_ELEM((*this), 2, 1)*MAT_ELEM((*this), 0, 0)-MAT_ELEM((*this), 2, 0)*MAT_ELEM((*this), 0, 1));
         	MAT_ELEM(result, 2, 2) =   MAT_ELEM((*this), 1, 1)*MAT_ELEM((*this), 0, 0)-MAT_ELEM((*this), 1, 0)*MAT_ELEM((*this), 0, 1);
-        	double tmp = MAT_ELEM((*this), 0, 0) * MAT_ELEM(result, 0, 0) +
+        	DOUBLE tmp = MAT_ELEM((*this), 0, 0) * MAT_ELEM(result, 0, 0) +
         			     MAT_ELEM((*this), 1, 0) * MAT_ELEM(result, 0, 1) +
         			     MAT_ELEM((*this), 2, 0) * MAT_ELEM(result, 0, 2);
         	result /= tmp;
@@ -1181,7 +1117,7 @@ public:
         	MAT_ELEM(result, 0, 1) = -MAT_ELEM((*this), 0, 1);
         	MAT_ELEM(result, 1, 0) = -MAT_ELEM((*this), 1, 0);
         	MAT_ELEM(result, 1, 1) =  MAT_ELEM((*this), 0, 0);
-        	double tmp = MAT_ELEM((*this), 0, 0) * MAT_ELEM((*this), 1, 1) -
+        	DOUBLE tmp = MAT_ELEM((*this), 0, 0) * MAT_ELEM((*this), 1, 1) -
 					     MAT_ELEM((*this), 0, 1) * MAT_ELEM((*this), 1, 0);
         	result /= tmp;
         }
@@ -1189,11 +1125,11 @@ public:
         {
 
 			// Perform SVD decomposition
-			Matrix2D< double > u, v;
-			Matrix1D< double > w;
+			Matrix2D< DOUBLE > u, v;
+			Matrix1D< DOUBLE > w;
 			svdcmp(*this, u, w, v); // *this = U * W * V^t
 
-			double tol = computeMax() * XMIPP_MAX(mdimx, mdimy) * 1e-14;
+			DOUBLE tol = computeMax() * XMIPP_MAX(mdimx, mdimy) * 1e-14;
 
 			// Compute W^-1
 			bool invertible = false;
@@ -1311,21 +1247,21 @@ void lubksb(const Matrix2D<T>& LU, Matrix1D< int >& indx, Matrix1D<T>& b)
 
 /** SVD Backsubstitution
  */
-void svbksb(Matrix2D< double >& u,
-            Matrix1D< double >& w,
-            Matrix2D< double >& v,
-            Matrix1D< double >& b,
-            Matrix1D< double >& x);
+void svbksb(Matrix2D< DOUBLE >& u,
+            Matrix1D< DOUBLE >& w,
+            Matrix2D< DOUBLE >& v,
+            Matrix1D< DOUBLE >& b,
+            Matrix1D< DOUBLE >& x);
 
 /** SVD Decomposition (through numerical recipes)
  */
 template<typename T>
 void svdcmp(const Matrix2D< T >& a,
-            Matrix2D< double >& u,
-            Matrix1D< double >& w,
-            Matrix2D< double >& v)
+            Matrix2D< DOUBLE >& u,
+            Matrix1D< DOUBLE >& w,
+            Matrix2D< DOUBLE >& v)
 {
-    // svdcmp only works with double
+    // svdcmp only works with DOUBLE
     typeCast(a, u);
 
     // Set size of matrices
@@ -1342,8 +1278,8 @@ void svdcmp(const Matrix2D< T >& a,
 /** Solve system of linear equations (Ax=b) through SVD Decomposition (through numerical recipes)
  */
 template<typename T>
-void solve(const Matrix2D< double >& A, const Matrix1D< double >& b,
-                  Matrix1D< double >& result, double tolerance)
+void solve(const Matrix2D< DOUBLE >& A, const Matrix1D< DOUBLE >& b,
+                  Matrix1D< DOUBLE >& result, DOUBLE tolerance)
 {
     if (A.mdimx == 0)
         REPORT_ERROR("Solve: Matrix is empty");
@@ -1359,8 +1295,8 @@ void solve(const Matrix2D< double >& A, const Matrix1D< double >& b,
 
     // First perform de single value decomposition
     // Xmipp interface that calls to svdcmp of numerical recipes
-    Matrix2D< double > u, v;
-    Matrix1D< double > w;
+    Matrix2D< DOUBLE > u, v;
+    Matrix1D< DOUBLE > w;
     svdcmp(A, u, w, v);
 
     // Here is checked if eigenvalues of the svd decomposition are acceptable
@@ -1374,7 +1310,7 @@ void solve(const Matrix2D< double >& A, const Matrix1D< double >& b,
     result.resize(b.vdim);
 
     // Xmipp interface that calls to svdksb of numerical recipes
-    Matrix1D< double > bd;
+    Matrix1D< DOUBLE > bd;
     typeCast(b, bd);
     svbksb(u, w, v, bd, result);
 }
@@ -1403,11 +1339,11 @@ void solve(const Matrix2D<T>& A, const Matrix2D<T>& b, Matrix2D<T>& result)
 
 /** Least-squares rigid transformation between two sets of 3D coordinates
  *
-double lsq_rigid_body_transformation(std::vector<Matrix1D<double> > &set1, std::vector<Matrix1D<double> > &set2,
-		Matrix2D<double> &Rot, Matrix1D<double> &trans)
+DOUBLE lsq_rigid_body_transformation(std::vector<Matrix1D<DOUBLE> > &set1, std::vector<Matrix1D<DOUBLE> > &set2,
+		Matrix2D<DOUBLE> &Rot, Matrix1D<DOUBLE> &trans)
 {
-	Matrix2D<double> A;
-	Matrix1D<double> avg1, avg2;
+	Matrix2D<DOUBLE> A;
+	Matrix1D<DOUBLE> avg1, avg2;
 
 	if (set1.size() != set2.size())
 		REPORT_ERROR("lsq_rigid_body_transformation ERROR: unequal set size");
@@ -1424,8 +1360,8 @@ double lsq_rigid_body_transformation(std::vector<Matrix1D<double> > &set1, std::
 		avg1 += set1[i];
 		avg2 += set2[i];
 	}
-	avg1 /= (double)set1.size();
-	avg2 /= (double)set1.size();
+	avg1 /= (DOUBLE)set1.size();
+	avg2 /= (DOUBLE)set1.size();
 
 	A.initZeros(3, 3);
 	Rot.initZeros(4,4);
@@ -1443,8 +1379,8 @@ double lsq_rigid_body_transformation(std::vector<Matrix1D<double> > &set1, std::
 		A(2, 2) += (ZZ(set1[i]) - ZZ(avg1)) * (ZZ(set2[i]) - ZZ(avg2));
 	}
 
-	Matrix2D< double > U, V;
-	Matrix1D< double > w;
+	Matrix2D< DOUBLE > U, V;
+	Matrix1D< DOUBLE > w;
 
 	// TODO: check inverse, transpose etc etc!!!
 
@@ -1456,7 +1392,7 @@ double lsq_rigid_body_transformation(std::vector<Matrix1D<double> > &set1, std::
 	trans = avg1 - Rot * avg2;
 
 	// return the squared difference term
-	double error = 0.;
+	DOUBLE error = 0.;
 	for (int i = 0; i < set1.size(); i++)
 	{
 		error += (Rot * set2[i] + trans - set1[i]).sum2();
@@ -1469,7 +1405,7 @@ double lsq_rigid_body_transformation(std::vector<Matrix1D<double> > &set1, std::
 
 /** Conversion from one type to another.
  *
- * If we have an integer array and we need a double one, we can use this
+ * If we have an integer array and we need a DOUBLE one, we can use this
  * function. The conversion is done through a type casting of each element
  * If n >= 0, only the nth volumes will be converted, otherwise all NSIZE volumes
  */
diff --git a/src/metadata_container.cpp b/src/metadata_container.cpp
index 338479a..80576b6 100644
--- a/src/metadata_container.cpp
+++ b/src/metadata_container.cpp
@@ -43,25 +43,13 @@
  ***************************************************************************/
 #include "src/metadata_container.h"
 
-MetaDataContainer::MetaDataContainer()
-{
-}
-;
-MetaDataContainer::~MetaDataContainer()
-{
-    clear();
-}
-;
-
-
-
 void MetaDataContainer::insertVoidPtr(EMDLabel name, void * value)
 {
     // If values[name] already existed, first free that memory
     if (values[name])
     {
-        if (EMDL::isDouble(name))
-            delete (double*)values[name];
+    	if (EMDL::isDouble(name))
+            delete (DOUBLE*)values[name];
         else if (EMDL::isInt(name))
             delete (int*)values[name];
         else if (EMDL::isLong(name))
@@ -69,7 +57,7 @@ void MetaDataContainer::insertVoidPtr(EMDLabel name, void * value)
         else if (EMDL::isBool(name))
             delete (bool*)values[name];
         else if (EMDL::isString(name))
-            delete (std::string*)values[name];
+        	delete (std::string*)values[name];
         else
             REPORT_ERROR("Unrecognised label type in MetaDataContainer clear");
     }
@@ -106,7 +94,7 @@ void MetaDataContainer::copy(const MetaDataContainer &MDc)
 
             if (EMDL::isDouble(lCode))
             {
-                addValue(lCode, *((double *) aux));
+                addValue(lCode, *((DOUBLE *) aux));
             }
             else if (EMDL::isString(lCode))
             {
@@ -141,60 +129,66 @@ MetaDataContainer::MetaDataContainer(const MetaDataContainer &MDc)
 }
 
 
-void MetaDataContainer::addValue(const std::string &name,
-        const std::string &value)
+void MetaDataContainer::addValueFromString(const EMDLabel &lCode, const std::string &value)
 {
-    EMDLabel lCode = EMDL::str2Label(name);
-    std::istringstream i(value);
-
-    // Look for a double value
-    if (EMDL::isDouble(lCode))
-    {
-        double doubleValue;
-
-        i >> doubleValue;
-
-        addValue(lCode, doubleValue);
-    }
-    else if (EMDL::isString(lCode))
-    {
-        addValue(lCode, value);
-    }
-    else if (EMDL::isInt(lCode))
-    {
-        int intValue;
 
-        i >> intValue;
-
-        addValue(lCode, intValue);
-    }
-    else if (EMDL::isLong(lCode))
-    {
-        long int longValue;
-
-        i >> longValue;
+	if (EMDL::isString(lCode))
+	{
+		addValue(lCode, value);
+	}
+	else
+	{
+		std::istringstream i(value);
+		// Look for a DOUBLE value
+		if (EMDL::isDouble(lCode))
+		{
+			DOUBLE DOUBLEValue;
+			i >> DOUBLEValue;
+			addValue(lCode, DOUBLEValue);
+		}
+		else if (EMDL::isInt(lCode))
+		{
+			int intValue;
+			i >> intValue;
+			addValue(lCode, intValue);
+		}
+		else if (EMDL::isLong(lCode))
+		{
+			long int longValue;
+			i >> longValue;
+			addValue(lCode, longValue);
+		}
+		else if (EMDL::isBool(lCode))
+		{
+			bool boolValue;
+			i >> boolValue;
+			addValue(lCode, boolValue);
+		}
+	}
+}
 
-        addValue(lCode, longValue);
-    }
-    else if (EMDL::isBool(lCode))
+#ifdef FLOAT_PRECISION
+void MetaDataContainer::addValue(EMDLabel name, const double &value)
+{
+    if (EMDL::isDouble(name))
     {
-        bool boolValue;
-
-        i >> boolValue;
-
-        addValue(lCode, boolValue);
+    	void * newValue = (void *) (new float(value));
+    	insertVoidPtr(name, newValue);
     }
+    else
+    	REPORT_ERROR("addValue for float: label " + EMDL::label2Str(name) + " is not of type float!");
 }
+#endif
 
-void MetaDataContainer::addValue(EMDLabel name, const double &value)
+void MetaDataContainer::addValue(EMDLabel name, const DOUBLE &value)
 {
     if (EMDL::isDouble(name))
     {
-    	void * newValue = (void *) (new double(value));
+    	void * newValue = (void *) (new DOUBLE(value));
     	insertVoidPtr(name, newValue);
     }
     else
-    	REPORT_ERROR("addValue for double: label " + EMDL::label2Str(name) + " is not of type double!");
+    	REPORT_ERROR("addValue for DOUBLE: label " + EMDL::label2Str(name) + " is not of type DOUBLE!");
 }
 
 void MetaDataContainer::addValue(EMDLabel name, const int &value)
@@ -247,7 +241,7 @@ void MetaDataContainer::addDefaultValue(EMDLabel name)
 	void * newValue;
 	if (EMDL::isDouble(name))
     {
-    	newValue = (void *) (new double(0.));
+    	newValue = (void *) (new DOUBLE(0.));
     }
     else if (EMDL::isInt(name) || EMDL::isLong(name))
     {
@@ -268,7 +262,7 @@ void MetaDataContainer::addDefaultValue(EMDLabel name)
 
 }
 
-bool MetaDataContainer::getValue( const EMDLabel name, double &value)
+bool MetaDataContainer::getValue( const EMDLabel name, DOUBLE &value)
 {
     std::map<EMDLabel, void *>::iterator element;
 
@@ -281,9 +275,9 @@ bool MetaDataContainer::getValue( const EMDLabel name, double &value)
     else
     {
     	if (EMDL::isDouble(element->first))
-    		value = *((double *) element->second);
+    		value = *((DOUBLE *) element->second);
     	else
-    		REPORT_ERROR("getValue for double: label " + EMDL::label2Str(element->first) + " is not of type double!");
+    		REPORT_ERROR("getValue for DOUBLE: label " + EMDL::label2Str(element->first) + " is not of type DOUBLE!");
 
     	return true;
     }
@@ -424,8 +418,8 @@ bool MetaDataContainer::writeValueToStream(std::ostream &outstream,
 #endif
 		if (EMDL::isDouble(inputLabel))
         {
-            double d;
-            d = *((double*) (getVoidPtr(inputLabel)));
+            DOUBLE d;
+            d = *((DOUBLE*) (getVoidPtr(inputLabel)));
             if ((ABS(d) > 0. && ABS(d) < 0.001) || ABS(d) > 100000.)
                 outstream << std::setw(12) << std::scientific;
             else
diff --git a/src/metadata_container.h b/src/metadata_container.h
index 9b2e8d0..5cdd5e6 100644
--- a/src/metadata_container.h
+++ b/src/metadata_container.h
@@ -55,8 +55,8 @@
 #include "src/metadata_label.h"
 
 //useful to init values to zero
-//static double zeroD=0.;
-//static double    oneD=1.;
+//static DOUBLE zeroD=0.;
+//static DOUBLE    oneD=1.;
 //static bool  falseb=false;
 
 class MetaDataContainer
@@ -76,21 +76,26 @@ public:
      */
     MetaDataContainer& operator =(const MetaDataContainer &MDc);
 
-    /** Constructor */
-    MetaDataContainer();
-    /** Copy constructor
-     *
-     */
+    /** Empty Constructor */
+    MetaDataContainer() {};
+
+    /** Copy constructor */
     MetaDataContainer(const MetaDataContainer &MDc);
 
     /** Destructor */
-    ~MetaDataContainer();
+    ~MetaDataContainer()
+    {
+    	clear();
+    }
 
     /** Create a new attribute-value pair of string type */
-    void addValue(const std::string &name, const std::string &value);
+    void addValueFromString(const EMDLabel &label, const std::string &value);
 
     /** Creates a new label-value pair, and checks the type of the label is the same as that of value */
+#ifdef FLOAT_PRECISION
     void addValue(EMDLabel name, const double &value);
+#endif
+    void addValue(EMDLabel name, const DOUBLE &value);
     void addValue(EMDLabel name, const int &value);
     void addValue(EMDLabel name, const long int &value);
     void addValue(EMDLabel name, const bool &value);
@@ -109,7 +114,7 @@ public:
              it != values.end(); ++it)
         {
         	if (EMDL::isDouble(it->first))
-                delete (double*)it->second;
+                delete (DOUBLE*)it->second;
             else if (EMDL::isInt(it->first))
                 delete (int*)it->second;
             else if (EMDL::isLong(it->first))
@@ -131,7 +136,7 @@ public:
      *  If the name does not exist in the container, the function returns false
      *
      */
-    bool getValue( const EMDLabel name, double &value);
+    bool getValue( const EMDLabel name, DOUBLE &value);
     bool getValue( const EMDLabel name, int &value);
     bool getValue( const EMDLabel name, long int &value);
     bool getValue( const EMDLabel name, bool &value);
diff --git a/src/metadata_label.cpp b/src/metadata_label.cpp
index 4f9fdc6..aee5597 100644
--- a/src/metadata_label.cpp
+++ b/src/metadata_label.cpp
@@ -70,7 +70,7 @@ void EMDL::printDefinitions(std::ostream& out)
 		else if (EMDL::isBool(names[strIt->first]))
 			out << " (bool)   ";
 		else if (EMDL::isDouble(names[strIt->first]))
-			out << " (double) ";
+			out << " (DOUBLE) ";
 		else if (EMDL::isString(names[strIt->first]))
 			out << " (string) ";
 		else
diff --git a/src/metadata_label.h b/src/metadata_label.h
index 9f92dd3..bc57a12 100644
--- a/src/metadata_label.h
+++ b/src/metadata_label.h
@@ -76,7 +76,7 @@ enum EMDLabel
     EMDL_CTF_CA, ///< Chromatic aberration
     EMDL_CTF_DETECTOR_PIXEL_SIZE, ///< Pixel size for detector as used in CTF-determination
     EMDL_CTF_ENERGY_LOSS, ///< Energy loss
-    EMDL_CTF_FOM, ///< ctffind3 FOM (CC) for quality of CTF-fit
+    EMDL_CTF_FOM, ///< ctffind FOM (CC) for quality of CTF-fit
     EMDL_CTF_IMAGE, ///< name of an image describing the CTF model
     EMDL_CTF_LENS_STABILITY, ///< Lens stability
     EMDL_CTF_MAGNIFICATION, ///< Magnification used for CTF-determination
@@ -88,6 +88,7 @@ enum EMDLabel
     EMDL_CTF_VALUE, ///< CTF value
 
     EMDL_IMAGE_NAME,
+    EMDL_IMAGE_ORI_NAME,
     EMDL_IMAGE_RECONSTRUCT_NAME,
     EMDL_IMAGE_ID,
     EMDL_IMAGE_ENABLED,
@@ -267,6 +268,7 @@ enum EMDLabel
 
     EMDL_POSTPROCESS_BFACTOR,
     EMDL_POSTPROCESS_FINAL_RESOLUTION,
+    EMDL_POSTPROCESS_FSC_GENERAL,
     EMDL_POSTPROCESS_FSC_TRUE,
     EMDL_POSTPROCESS_FSC_MASKED,
     EMDL_POSTPROCESS_FSC_UNMASKED,
@@ -411,11 +413,12 @@ private:
         EMDL::addLabel(EMDL_CTF_VALUE, EMDL_DOUBLE, "rlnCtfValue", "Value of the Contrast Transfer Function");
 
     	EMDL::addLabel(EMDL_IMAGE_NAME, EMDL_STRING, "rlnImageName", "Name of an image");
+    	EMDL::addLabel(EMDL_IMAGE_ORI_NAME, EMDL_STRING, "rlnImageOriginalName", "Original name of an image");
     	EMDL::addLabel(EMDL_IMAGE_RECONSTRUCT_NAME, EMDL_STRING, "rlnReconstructImageName", "Name of an image to be used for reconstruction only");
     	EMDL::addLabel(EMDL_IMAGE_ID, EMDL_LONG, "rlnImageId", "ID (i.e. a unique number) of an image");
     	EMDL::addLabel(EMDL_IMAGE_ENABLED, EMDL_BOOL, "rlnEnabled", "Not used in RELION, only included for backward compatibility with XMIPP selfiles");
-        EMDL::addLabel(EMDL_IMAGE_DATATYPE, EMDL_INT, "rlnDataType", "Type of data stored in an image (e.g. int, double etc)");
-        EMDL::addLabel(EMDL_IMAGE_DIMENSIONALITY, EMDL_INT, "rlnDataDimensionality", "Dimensionality of data stored in an image (i.e. 2 or 3)");
+        EMDL::addLabel(EMDL_IMAGE_DATATYPE, EMDL_INT, "rlnDataType", "Type of data stored in an image (e.g. int, DOUBLE etc)");
+        EMDL::addLabel(EMDL_IMAGE_DIMENSIONALITY, EMDL_INT, "rlnImageDimensionality", "Dimensionality of data stored in an image (i.e. 2 or 3)");
         EMDL::addLabel(EMDL_IMAGE_BEAMTILT_X, EMDL_DOUBLE, "rlnBeamTiltX", "Beam tilt in the X-direction (in mrad)");
         EMDL::addLabel(EMDL_IMAGE_BEAMTILT_Y, EMDL_DOUBLE, "rlnBeamTiltY", "Beam tilt in the Y-direction (in mrad)");
         EMDL::addLabel(EMDL_IMAGE_BEAMTILT_GROUP, EMDL_STRING, "rlnBeamTiltGroupName", "Name of a group (of images) with assumedly identical beam-tilts");
@@ -590,6 +593,7 @@ private:
 
         EMDL::addLabel(EMDL_POSTPROCESS_FINAL_RESOLUTION, EMDL_DOUBLE, "rlnFinalResolution", "Final estimated resolution after postprocessing (in Angstroms)");
         EMDL::addLabel(EMDL_POSTPROCESS_BFACTOR, EMDL_DOUBLE, "rlnBfactorUsedForSharpening", "Applied B-factor in the sharpening of the map");
+        EMDL::addLabel(EMDL_POSTPROCESS_FSC_GENERAL, EMDL_DOUBLE, "rlnFourierShellCorrelation", "FSC value (of unspecified type, e.g. masked or unmasked)");
         EMDL::addLabel(EMDL_POSTPROCESS_FSC_TRUE, EMDL_DOUBLE, "rlnFourierShellCorrelationCorrected", "Final FSC value: i.e. after correction based on masking of randomized-phases maps");
         EMDL::addLabel(EMDL_POSTPROCESS_FSC_MASKED, EMDL_DOUBLE, "rlnFourierShellCorrelationMaskedMaps", "FSC value after masking of the original maps");
         EMDL::addLabel(EMDL_POSTPROCESS_FSC_UNMASKED, EMDL_DOUBLE, "rlnFourierShellCorrelationUnmaskedMaps", "FSC value before masking of the original maps");
diff --git a/src/metadata_table.cpp b/src/metadata_table.cpp
index db5d1fe..d78e9d2 100644
--- a/src/metadata_table.cpp
+++ b/src/metadata_table.cpp
@@ -44,6 +44,72 @@
 
 #include "src/metadata_table.h"
 
+// Label for sorting
+static EMDLabel sortlabel;
+bool compareString(MetaDataContainer *lh, MetaDataContainer *rh)
+{
+	std::string lhstr, rhstr;
+	lh->getValue(sortlabel, lhstr);
+	rh->getValue(sortlabel, rhstr);
+	return lhstr < rhstr;
+}
+bool compareStringNoAt(MetaDataContainer *lh, MetaDataContainer *rh)
+{
+	std::string lhstr, rhstr;
+	lh->getValue(sortlabel, lhstr);
+	rh->getValue(sortlabel, rhstr);
+	lhstr = lhstr.substr(lhstr.find("@")+1);
+	rhstr = rhstr.substr(rhstr.find("@")+1);
+	return lhstr < rhstr;
+}
+bool compareDouble(MetaDataContainer *lh, MetaDataContainer *rh)
+{
+	DOUBLE lhstr, rhstr;
+	lh->getValue(sortlabel, lhstr);
+	rh->getValue(sortlabel, rhstr);
+	return lhstr < rhstr;
+}
+bool compareInteger(MetaDataContainer *lh, MetaDataContainer *rh)
+{
+	int lhstr, rhstr;
+	lh->getValue(sortlabel, lhstr);
+	rh->getValue(sortlabel, rhstr);
+	return lhstr < rhstr;
+}
+bool compareLong(MetaDataContainer *lh, MetaDataContainer *rh)
+{
+	long lhstr, rhstr;
+	lh->getValue(sortlabel, lhstr);
+	rh->getValue(sortlabel, rhstr);
+	return lhstr < rhstr;
+}
+
+void MetaDataTable::newSort(const EMDLabel label, bool do_reverse, bool do_sort_after_at)
+{
+
+	sortlabel = label;
+	if (EMDL::isString(label))
+	{
+		if (do_sort_after_at)
+			std::stable_sort(objects.begin(), objects.end(), compareStringNoAt);
+		else
+			std::stable_sort(objects.begin(), objects.end(), compareString);
+	}
+	else if (EMDL::isDouble(label))
+		std::stable_sort(objects.begin(), objects.end(), compareDouble);
+	else if (EMDL::isInt(label))
+		std::stable_sort(objects.begin(), objects.end(), compareInteger);
+	else if (EMDL::isLong(label))
+		std::stable_sort(objects.begin(), objects.end(), compareLong);
+	else
+		REPORT_ERROR("Cannot sort this label: " + EMDL::label2Str(label));
+
+	if (do_reverse)
+		std::reverse(objects.begin(), objects.end());
+
+}
+
+
 MetaDataTable::MetaDataTable()
 {
     clear();
@@ -156,49 +222,23 @@ std::string MetaDataTable::getName() const
     return name;
 }
 
-bool MetaDataTable::setValue(const std::string &name, const std::string &value,
+bool MetaDataTable::setValueFromString(const EMDLabel &label, const std::string &value,
                         long int objectID)
 {
-    EMDLabel label = EMDL::str2Label(name);
-
-    if (!isEmpty() && EMDL::isValidLabel(label))
-    {
-        long int auxID = (objectID == -1) ? current_objectID : objectID;
-
-        if (auxID >= objects.size())
-        	REPORT_ERROR("MetaDataTable::setValue: auxID >= objects.size()");
-
-        MetaDataContainer * aux = objects[auxID];
 
-        // Check whether label is correct (belongs to the enum in the metadata_container header
-        // and whether it is present in the activeLabels vector. If not, add it to all the other
-        // objects with default values
-        std::vector<EMDLabel>::iterator location;
-        location = std::find(activeLabels.begin(), activeLabels.end(), label);
+	if (objectID == -1)
+		objectID = current_objectID;
 
-        if (location == activeLabels.end())
-        {
-            activeLabels.push_back(label);
+	if (objectID >= objects.size())
+		REPORT_ERROR("MetaDataTable::setValueFromString: objectID >= objects.size()");
 
-            // Add this label to the rest of the objects in this class
-            for (long int idx = 0; idx < objects.size(); idx++)
-            {
-                if (objects[idx] != aux)
-                	objects[idx]->addDefaultValue(label);
-            }
-        }
+	objects[objectID]->addValueFromString(label, value);
 
-        aux->addValue(name, value);
+	return true;
 
-        return true;
-    }
-    else
-    {
-    	// in case of empty objects or invalid label
-        return false;
-    }
 }
 
+
 bool MetaDataTable::containsLabel(const EMDLabel label) const
 {
     return vectorContainsLabel(activeLabels, label);
@@ -241,7 +281,7 @@ long int MetaDataTable::addObject(MetaDataContainer * data, long int objectID)
 
     if (objectID == -1)
     {
-        result = objects.size();
+    	result = objects.size();
         objects.resize(result+1);
     }
     else
@@ -269,11 +309,13 @@ long int MetaDataTable::addObject(MetaDataContainer * data, long int objectID)
     // Set default values for the existing labels
     if (data == NULL)
     {
-		std::vector<EMDLabel>::iterator It;
+    	/* Sjors 21nov2014: I don't see why this would be necessary...
+    	std::vector<EMDLabel>::iterator It;
 		for (It = activeLabels.begin(); It != activeLabels.end(); It++)
 		{
 			(objects[result])->addDefaultValue(*It);
 		}
+		*/
     }
     else
     {
@@ -538,7 +580,7 @@ void MetaDataTable::readStarLoop(std::ifstream& in, std::vector<EMDLabel> *desir
 				labelPosition++;
 				continue;
 			}
-			setValue(EMDL::label2Str(activeLabels[labelPosition - counterIgnored]), value);
+			setValueFromString(activeLabels[labelPosition - counterIgnored], value);
 			labelPosition++;
 		}
 
@@ -580,7 +622,7 @@ bool MetaDataTable::readStarList(std::ifstream& in, std::vector<EMDLabel> *desir
     		 if (label != EMDL_UNDEFINED)
 			 {
 				 activeLabels.push_back(label);
-				 setValue(EMDL::label2Str(label), value, objectID);
+				 setValueFromString(label, value, objectID);
 			 }
     	 }
     	 // Check whether there is a comment or an empty line
@@ -798,4 +840,149 @@ void MetaDataTable::writeValueToString(std::string & result,
 
 
 
+void compareMetaDataTable(MetaDataTable &MD1, MetaDataTable &MD2,
+		MetaDataTable &MDboth, MetaDataTable &MDonly1, MetaDataTable &MDonly2,
+		EMDLabel label1, DOUBLE eps, EMDLabel label2, EMDLabel label3)
+{
+	if (!MD1.containsLabel(label1))
+		REPORT_ERROR("compareMetaDataTableEqualLabel::ERROR MD1 does not contain the specified label1.");
+	if (!MD2.containsLabel(label1))
+		REPORT_ERROR("compareMetaDataTableEqualLabel::ERROR MD2 does not contain the specified label1.");
+
+	if (label2 != EMDL_UNDEFINED)
+	{
+		if (!EMDL::isDouble(label1) || !EMDL::isDouble(label2))
+			REPORT_ERROR("compareMetaDataTableEqualLabel::ERROR 2D or 3D distances are only allowed for DOUBLEs.");
+		if (!MD1.containsLabel(label2))
+			REPORT_ERROR("compareMetaDataTableEqualLabel::ERROR MD1 does not contain the specified label2.");
+		if (!MD2.containsLabel(label2))
+			REPORT_ERROR("compareMetaDataTableEqualLabel::ERROR MD2 does not contain the specified label2.");
+	}
+
+	if (label3 != EMDL_UNDEFINED)
+	{
+		if (!EMDL::isDouble(label3))
+			REPORT_ERROR("compareMetaDataTableEqualLabel::ERROR 3D distances are only allowed for DOUBLEs.");
+		if (!MD1.containsLabel(label3))
+			REPORT_ERROR("compareMetaDataTableEqualLabel::ERROR MD1 does not contain the specified label3.");
+		if (!MD2.containsLabel(label3))
+			REPORT_ERROR("compareMetaDataTableEqualLabel::ERROR MD2 does not contain the specified label3.");
+	}
+
+	MDboth.clear();
+	MDonly1.clear();
+	MDonly2.clear();
+
+	std::string mystr1, mystr2;
+	int myint1, myint2;
+	DOUBLE myd1, myd2, mydy1 = 0., mydy2 = 0., mydz1 = 0., mydz2 = 0.;
+
+
+	// loop over MD1
+	std::vector<long int> to_remove_from_only2;
+	for (long int current_object1 = MD1.firstObject();
+	              current_object1 != MetaDataTable::NO_MORE_OBJECTS && current_object1 != MetaDataTable::NO_OBJECTS_STORED;
+	              current_object1 = MD1.nextObject())
+	{
+		if (EMDL::isString(label1))
+			MD1.getValue(label1, mystr1);
+		else if (EMDL::isInt(label1))
+			MD1.getValue(label1, myint1);
+		else if (EMDL::isDouble(label1))
+		{
+			MD1.getValue(label1, myd1);
+			if (label2 != EMDL_UNDEFINED)
+				MD1.getValue(label2, mydy1);
+			if (label3 != EMDL_UNDEFINED)
+				MD1.getValue(label3, mydz1);
+		}
+		else
+			REPORT_ERROR("compareMetaDataTableEqualLabel ERROR: only implemented for strings, integers or DOUBLEs");
+
+		// loop over MD2
+		bool have_in_2 = false;
+		for (long int current_object2 = MD2.firstObject();
+		              current_object2 != MetaDataTable::NO_MORE_OBJECTS && current_object2 != MetaDataTable::NO_OBJECTS_STORED;
+		              current_object2 = MD2.nextObject())
+		{
+
+			if (EMDL::isString(label1))
+			{
+				MD2.getValue(label1, mystr2);
+				if (strcmp(mystr1.c_str(), mystr2.c_str()) == 0)
+				{
+					have_in_2 = true;
+					to_remove_from_only2.push_back(current_object2);
+					MDboth.addObject(MD1.getObject());
+					break;
+				}
+			}
+			else if (EMDL::isInt(label1))
+			{
+				MD2.getValue(label1, myint2);
+				if ( ABS(myint2 - myint1) <= ROUND(eps) )
+				{
+					have_in_2 = true;
+					to_remove_from_only2.push_back(current_object2);
+					MDboth.addObject(MD1.getObject());
+					break;
+				}
+			}
+			else if (EMDL::isDouble(label1))
+			{
+				MD2.getValue(label1, myd2);
+				if (label2 != EMDL_UNDEFINED)
+					MD2.getValue(label2, mydy2);
+				if (label3 != EMDL_UNDEFINED)
+					MD2.getValue(label3, mydz2);
+
+				DOUBLE dist = sqrt( (myd1 - myd2) * (myd1 - myd2) +
+						            (mydy1 - mydy2) * (mydy1 - mydy2) +
+						            (mydz1 - mydz2) * (mydz1 - mydz2) );
+				if ( ABS(dist) <= eps )
+				{
+					have_in_2 = true;
+					to_remove_from_only2.push_back(current_object2);
+					//std::cerr << " current_object1= " << current_object1 << std::endl;
+					//std::cerr << " myd1= " << myd1 << " myd2= " << myd2 << " mydy1= " << mydy1 << " mydy2= " << mydy2 << " dist= "<<dist<<std::endl;
+					//std::cerr << " to be removed current_object2= " << current_object2 << std::endl;
+					MDboth.addObject(MD1.getObject());
+					break;
+				}
+			}
+		}
+
+		if (!have_in_2)
+		{
+			MDonly1.addObject(MD1.getObject());
+		}
+	}
+
+
+
+	for (long int current_object2 = MD2.firstObject();
+				current_object2 != MetaDataTable::NO_MORE_OBJECTS && current_object2 != MetaDataTable::NO_OBJECTS_STORED;
+				current_object2 = MD2.nextObject())
+	{
+
+		bool to_be_removed = false;
+		for (long int i = 0; i < to_remove_from_only2.size(); i++)
+		{
+			if (to_remove_from_only2[i] == current_object2)
+			{
+				to_be_removed = true;
+				break;
+			}
+		}
+		if (!to_be_removed)
+		{
+			//std::cerr << " doNOT remove current_object2= " << current_object2 << std::endl;
+			MDonly2.addObject(MD2.getObject(current_object2));
+		}
+	}
+
+
+}
+
+
 
diff --git a/src/metadata_table.h b/src/metadata_table.h
index 00c804f..a282c43 100644
--- a/src/metadata_table.h
+++ b/src/metadata_table.h
@@ -63,7 +63,7 @@
 /** For all objects.
  @code
  FOR_ALL_OBJECTS_IN_METADATA(metadata) {
-   double rot;
+   DOUBLE rot;
    DF.getValue( EMDL_ANGLEROT, rot);
  }
  @endcode
@@ -196,8 +196,7 @@ public:
 
     // Read/set a new pair/value for an specified object. If no objectID is given, that
     // pointed by the class iterator is used
-    bool setValue(const std::string &name, const std::string &value,
-                  long int objectID = -1);
+    bool setValueFromString(const EMDLabel &label, const std::string &value, long int objectID = -1);
 
     template<class T>
     bool setValue(EMDLabel name, const T &value, long int objectID=-1)
@@ -239,29 +238,32 @@ public:
         }
     }
 
+    // No copying of entire MetaDataTable involved!
+    void newSort(const EMDLabel name, bool do_reverse = false, bool do_sort_after_at = false);
+
     // Sort the order of the elements based on the values in the input label (only numbers, no strings/bools!)
     // Have to pass dummy T parameter to get the template thing running?
     void sort(EMDLabel name, bool do_reverse = false, bool only_set_index = false)
     {
     	if ( !(EMDL::isInt(name) || EMDL::isLong(name) || EMDL::isDouble(name)) )
     		REPORT_ERROR("MetadataTable::sort%% ERROR: can only sorted numbers");
-    	std::vector<std::pair<double,long int> > vp;
+    	std::vector<std::pair<DOUBLE,long int> > vp;
     	vp.reserve(objects.size());
     	long int i = 0;
     	FOR_ALL_OBJECTS_IN_METADATA_TABLE(*this)
     	{
-    		double dval;
+    		DOUBLE dval;
     		if (EMDL::isInt(name))
     		{
     			int val;
     			getValue(name, val);
-    			dval = (double) val;
+    			dval = (DOUBLE) val;
     		}
     		else if (EMDL::isLong(name))
     		{
     			long int val;
     			getValue(name, val);
-    			dval = (double) val;
+    			dval = (DOUBLE) val;
     		}
     		else
     		{
@@ -396,4 +398,10 @@ public:
 
 };
 
+void compareMetaDataTable(MetaDataTable &MD1, MetaDataTable &MD2,
+		MetaDataTable &MDboth, MetaDataTable &MDonly1, MetaDataTable &MDonly2,
+		EMDLabel label1, DOUBLE eps = 0., EMDLabel label2 = EMDL_UNDEFINED, EMDLabel label3 = EMDL_UNDEFINED);
+
+
+
 #endif
diff --git a/src/ml_model.cpp b/src/ml_model.cpp
index 1b2ff39..b042a3c 100644
--- a/src/ml_model.cpp
+++ b/src/ml_model.cpp
@@ -24,12 +24,12 @@ void MlModel::initialise()
 {
 
 	// Auxiliary vector with relevant size in Fourier space
-	MultidimArray<double > aux;
+	MultidimArray<DOUBLE > aux;
     aux.initZeros(ori_size / 2 + 1);
 
 	// Now resize all relevant vectors
     Iref.resize(nr_classes);
-    pdf_class.resize(nr_classes, 1./(double)nr_classes);
+    pdf_class.resize(nr_classes, 1./(DOUBLE)nr_classes);
     pdf_direction.resize(nr_classes);
     group_names.resize(nr_groups, "");
     sigma2_noise.resize(nr_groups, aux);
@@ -47,13 +47,13 @@ void MlModel::initialise()
 
 	if (ref_dim==2)
 	{
-		Matrix1D<double> empty(2);
+		Matrix1D<DOUBLE> empty(2);
 		prior_offset_class.resize(nr_classes, empty);
 	}
 	// These arrays will be resized when they are filled
 	orientability_contrib.resize(nr_classes);
 
-	Projector ref(ori_size, interpolator, padding_factor, r_min_nn);
+	Projector ref(ori_size, interpolator, padding_factor, r_min_nn, data_dim);
     PPref.clear();
     // Now fill the entire vector with instances of "ref"
     PPref.resize(nr_classes, ref);
@@ -98,6 +98,10 @@ void MlModel::read(FileName fn_in)
 		!MDlog.getValue(EMDL_MLMODEL_AVE_PMAX, ave_Pmax) )
 		REPORT_ERROR("MlModel::readStar: incorrect model_general table");
 
+    // Retain compability with model files written by Relion prior to 1.4
+    if (!MDlog.getValue(EMDL_MLMODEL_DIMENSIONALITY_DATA, data_dim))
+        data_dim=2;
+
     // Take inverse again of current resolution:
     current_resolution = 1. / current_resolution;
 
@@ -111,7 +115,7 @@ void MlModel::read(FileName fn_in)
 
 	// Read classes
 	FileName fn_tmp;
-	Image<double> img;
+	Image<DOUBLE> img;
 	MDclass.readStar(in, "model_classes");
 	int iclass = 0;
 	FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDclass)
@@ -196,8 +200,8 @@ void MlModel::read(FileName fn_in)
 		{
 			MDclass.readStar(in, "model_pdf_orient_class_" + integerToString(iclass + 1));
 			pdf_direction[iclass].clear();
-			double aux;
-			std::vector<double> vaux;
+			DOUBLE aux;
+			std::vector<DOUBLE> vaux;
 			vaux.clear();
 			FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDclass)
 			{
@@ -230,18 +234,18 @@ void MlModel::read(FileName fn_in)
 
 }
 
-void MlModel::write(FileName fn_out, HealpixSampling &sampling)
+void MlModel::write(FileName fn_out, HealpixSampling &sampling, bool do_write_bild)
 {
 
 	MetaDataTable MDclass, MDgroup, MDlog, MDsigma;
     FileName fn_tmp, fn_tmp2;
-    double aux;
+    DOUBLE aux;
     std::ofstream  fh;
 
     // A. Write images
     if (ref_dim == 2)
     {
-    	Image<double> img(XSIZE(Iref[0]), YSIZE(Iref[0]), 1, nr_classes);
+    	Image<DOUBLE> img(XSIZE(Iref[0]), YSIZE(Iref[0]), 1, nr_classes);
     	for (int iclass = 0; iclass < nr_classes; iclass++)
     	{
     		FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY2D(Iref[iclass])
@@ -253,7 +257,7 @@ void MlModel::write(FileName fn_out, HealpixSampling &sampling)
     }
     else
     {
-    	Image<double> img;
+    	Image<DOUBLE> img;
     	// Set correct voxel size in the header
 		img.MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_X, pixel_size);
 		img.MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Y, pixel_size);
@@ -267,16 +271,19 @@ void MlModel::write(FileName fn_out, HealpixSampling &sampling)
 
     	}
 
-    	// Also write out bild files with the orientational distribution of each class
-		// Also write out angular distributions
-		for (int iclass = 0; iclass < nr_classes; iclass++)
-		{
-			FileName fn_bild;
-			fn_bild.compose(fn_out+"_class",iclass+1,"", 3);
-			fn_bild += "_angdist.bild";
-			double offset = ori_size * pixel_size / 2.;
-			sampling.writeBildFileOrientationalDistribution(pdf_direction[iclass], fn_bild, offset, offset);
-		}
+    	if (do_write_bild)
+    	{
+			// Also write out bild files with the orientational distribution of each class
+			// Also write out angular distributions
+			for (int iclass = 0; iclass < nr_classes; iclass++)
+			{
+				FileName fn_bild;
+				fn_bild.compose(fn_out+"_class",iclass+1,"", 3);
+				fn_bild += "_angdist.bild";
+				DOUBLE offset = ori_size * pixel_size / 2.;
+				sampling.writeBildFileOrientationalDistribution(pdf_direction[iclass], fn_bild, offset, offset);
+			}
+    	}
 
 	}
 
@@ -291,6 +298,7 @@ void MlModel::write(FileName fn_out, HealpixSampling &sampling)
 	MDlog.addObject();
 	MDlog.setName("model_general");
 	MDlog.setValue(EMDL_MLMODEL_DIMENSIONALITY, ref_dim);
+	MDlog.setValue(EMDL_MLMODEL_DIMENSIONALITY_DATA, data_dim);
 	MDlog.setValue(EMDL_MLMODEL_ORIGINAL_SIZE, ori_size);
 	MDlog.setValue(EMDL_MLMODEL_CURRENT_RESOLUTION, 1./current_resolution);
 	MDlog.setValue(EMDL_MLMODEL_CURRENT_SIZE, current_size);
@@ -418,7 +426,7 @@ void MlModel::write(FileName fn_out, HealpixSampling &sampling)
 void  MlModel::readTauSpectrum(FileName fn_tau, int verb)
 {
 	MetaDataTable MDtau;
-	double val;
+	DOUBLE val;
 	int idx;
 	MDtau.read(fn_tau);
 	FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDtau)
@@ -446,10 +454,13 @@ void MlModel::readImages(FileName fn_ref, int _ori_size, Experiment &_mydata,
 	// Set some stuff
 	nr_groups = _mydata.groups.size();
 	ori_size = _ori_size;
-	double avg_norm_correction = 1.;
+	DOUBLE avg_norm_correction = 1.;
+
+	// Data dimensionality
+	_mydata.MDexp.getValue(EMDL_IMAGE_DIMENSIONALITY, data_dim);
 
 	// Read references into memory
-	Image<double> img;
+	Image<DOUBLE> img;
 	FileName fn_tmp;
 	if (fn_ref != "None")
 	{
@@ -503,8 +514,13 @@ void MlModel::readImages(FileName fn_ref, int _ori_size, Experiment &_mydata,
 		do_average_unaligned = true;
 		do_generate_seeds = true;
 		refs_are_ctf_corrected = false;
-		img().initZeros(ori_size, ori_size);
-		ref_dim = 2;
+		if (data_dim == 2)
+			img().initZeros(ori_size, ori_size);
+		else if (data_dim == 3)
+			img().initZeros(ori_size, ori_size, ori_size);
+		else
+			REPORT_ERROR("MlOptimiser::read: ERROR data_dim should be either 2 or 3");
+		ref_dim = data_dim;
 		for (int iclass = 0; iclass < nr_classes; iclass++)
 			Iref.push_back(img());
 	}
@@ -569,27 +585,24 @@ void MlModel::expandToMovieFrames(Experiment &moviedata, int running_avg_side)
 
 	for (long int ipart = 0; ipart < moviedata.particles.size(); ipart++)
 	{
-		for (int iimg = 0; iimg < (moviedata.particles[ipart]).images.size(); iimg++)
-		{
-			long int group_id = ((moviedata.particles[ipart]).images[iimg]).group_id;
-			long int img_id = ((moviedata.particles[ipart]).images[iimg]).id;
-			// count the number of particles in this group
-			moviemodel.nr_particles_group[group_id]++;
-			// Get and check the number of frames is constant within one group
-			int nframes = -1;
-			if (!moviedata.MDimg.getValue(EMDL_PARTICLE_NR_FRAMES, nframes, img_id))
-				REPORT_ERROR("MlModel::expandToMovieFrames ERROR: cannot find rlnNrOfFrames in moviedata.");
-			if (nr_frames_in_group[group_id] < 0)
-				nr_frames_in_group[group_id] = nframes;
-			else if (nr_frames_in_group[group_id] != nframes)
-				REPORT_ERROR((std::string)"MlModel::expandToMovieFrames ERROR: unequal number of frames in group" + moviemodel.group_names[group_id]);
-		}
+		long int group_id = (moviedata.particles[ipart]).group_id;
+		long int part_id = (moviedata.particles[ipart]).id;
+		// count the number of particles in this group
+		moviemodel.nr_particles_group[group_id]++;
+		// Get and check the number of frames is constant within one group
+		int nframes = -1;
+		if (!moviedata.MDimg.getValue(EMDL_PARTICLE_NR_FRAMES, nframes, part_id))
+			REPORT_ERROR("MlModel::expandToMovieFrames ERROR: cannot find rlnNrOfFrames in moviedata.");
+		if (nr_frames_in_group[group_id] < 0)
+			nr_frames_in_group[group_id] = nframes;
+		else if (nr_frames_in_group[group_id] != nframes)
+			REPORT_ERROR((std::string)"MlModel::expandToMovieFrames ERROR: unequal number of frames in group" + moviemodel.group_names[group_id]);
 	}
 
 	// Correct the input sigma2noise spectra by a factor of nframes
 	for (int i=0; i < moviemodel.nr_groups; i++)
 	{
-		moviemodel.sigma2_noise[i] *= (double)nr_frames_in_group[i]/((double)(2 * running_avg_side + 1));
+		moviemodel.sigma2_noise[i] *= (DOUBLE)nr_frames_in_group[i]/((DOUBLE)(2 * running_avg_side + 1));
 	}
 
 	// Now replace the current model with the expanded moviemodel
@@ -608,7 +621,7 @@ void MlModel::initialisePdfDirection(int newsize)
 		if (oldsize == 0 || oldsize != newsize)
 		{
 			pdf_direction[iclass].resize(newsize);
-			pdf_direction[iclass].initConstant(1./((double) nr_classes * newsize));
+			pdf_direction[iclass].initConstant(1./((DOUBLE) nr_classes * newsize));
 		}
 	}
 	nr_directions = newsize;
@@ -626,7 +639,7 @@ void MlModel::setFourierTransformMaps(bool update_tau2_spectra, int nr_threads)
         }
         else
         {
-        	MultidimArray<double> dummy;
+        	MultidimArray<DOUBLE> dummy;
         	PPref[iclass].computeFourierTransformMap(Iref[iclass], dummy, current_size, nr_threads);
         }
     }
@@ -637,23 +650,23 @@ void MlModel::initialiseDataVersusPrior(bool fix_tau)
 {
 
     // Get total number of particles
-	double nr_particles = 0.;
+	DOUBLE nr_particles = 0.;
 	for (int igroup = 0; igroup < nr_particles_group.size(); igroup++)
-		nr_particles += (double)nr_particles_group[igroup];
+		nr_particles += (DOUBLE)nr_particles_group[igroup];
 
 	// Calculate average sigma2_noise over all image groups
-	MultidimArray<double> avg_sigma2_noise;
+	MultidimArray<DOUBLE> avg_sigma2_noise;
 	avg_sigma2_noise.initZeros(sigma2_noise[0]);
 	for (int igroup = 0; igroup < nr_particles_group.size(); igroup++)
 	{
-		avg_sigma2_noise += (double)(nr_particles_group[igroup]) * sigma2_noise[igroup];
+		avg_sigma2_noise += (DOUBLE)(nr_particles_group[igroup]) * sigma2_noise[igroup];
 	}
 	avg_sigma2_noise /= nr_particles;
 
 	// Get the FT of all reference structures
     // The Fourier Transforms are all "normalised" for 2D transforms of size = ori_size x ori_size
     // And spectrum is squared, so ori_size*ori_size in the 3D case!
-	double normfft = (ref_dim == 3) ? (double)(ori_size * ori_size) : 1.;
+	DOUBLE normfft = (ref_dim == 3 && data_dim == 2) ? (DOUBLE)(ori_size * ori_size) : 1.;
 
     for (int iclass = 0; iclass < nr_classes; iclass++)
 	{
@@ -661,7 +674,7 @@ void MlModel::initialiseDataVersusPrior(bool fix_tau)
 		tau2_class[iclass].resize(sigma2_noise[0]);
 
 		// Get the power spectrum of the reference
-		MultidimArray<double> spectrum(sigma2_noise[0]);
+		MultidimArray<DOUBLE> spectrum(sigma2_noise[0]);
 		getSpectrum(Iref[iclass], spectrum, POWER_SPECTRUM);
 
 		// Factor two because of two-dimensionality of the complex plane
@@ -683,12 +696,12 @@ void MlModel::initialiseDataVersusPrior(bool fix_tau)
 		fsc_halves_class[iclass].initZeros(sigma2_noise[0]);
 		FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY1D(tau2_class[iclass])
 		{
-			double evidence = nr_particles * pdf_class[iclass] / DIRECT_A1D_ELEM(avg_sigma2_noise, i);
+			DOUBLE evidence = nr_particles * pdf_class[iclass] / DIRECT_A1D_ELEM(avg_sigma2_noise, i);
 			// empirical accounting for ratio of pixels in 3D shells compared to 2D shells
 			if (ref_dim == 3 && i > 0)
-				evidence /= (2. * (double)i);
-			double prior = 1. /  DIRECT_A1D_ELEM(tau2_class[iclass], i);
-			double myssnr = evidence / prior;
+				evidence /= (2. * (DOUBLE)i);
+			DOUBLE prior = 1. /  DIRECT_A1D_ELEM(tau2_class[iclass], i);
+			DOUBLE myssnr = evidence / prior;
 			DIRECT_A1D_ELEM(data_vs_prior_class[iclass], i ) = myssnr;
 			// Also initialise FSC-halves here (...)
 			//DIRECT_A1D_ELEM(fsc_halves_class[iclass], i ) = myssnr / (myssnr + 1);
@@ -704,6 +717,7 @@ void MlWsumModel::initialise(MlModel &_model, FileName fn_sym)
     nr_groups = _model.nr_groups;
     nr_directions = _model.nr_directions;
     ref_dim = _model.ref_dim;
+    data_dim = _model.data_dim;
     ori_size = _model.ori_size;
     pdf_class = _model.pdf_class;
     if (ref_dim == 2)
@@ -730,13 +744,13 @@ void MlWsumModel::initialise(MlModel &_model, FileName fn_sym)
     orientability_contrib.clear();
 
 
-    MultidimArray<double> aux(ori_size / 2 + 1);
+    MultidimArray<DOUBLE> aux(ori_size / 2 + 1);
     wsum_signal_product_spectra.resize(nr_groups, aux);
     wsum_reference_power_spectra.resize(nr_groups, aux);
 
     // Resize MlWsumModel-specific vectors
     BackProjector BP(ori_size, ref_dim, fn_sym, interpolator, padding_factor, r_min_nn,
-    		         ML_BLOB_ORDER, ML_BLOB_RADIUS, ML_BLOB_ALPHA);
+    		         ML_BLOB_ORDER, ML_BLOB_RADIUS, ML_BLOB_ALPHA, data_dim);
     BPref.clear();
     BPref.resize(nr_classes, BP);
     sumw_group.resize(nr_groups);
@@ -783,7 +797,7 @@ void MlWsumModel::initZeros()
 #define MAX_PACK_SIZE 671010000
 #endif
 
-void MlWsumModel::pack(MultidimArray<double> &packed)
+void MlWsumModel::pack(MultidimArray<DOUBLE> &packed)
 {
 	// for LL & avePmax & sigma2_offset & avg_norm_correction & sigma2_rot & sigma2_tilt & sigma2_psi
 	long long int packed_size = 0;
@@ -887,7 +901,7 @@ void MlWsumModel::pack(MultidimArray<double> &packed)
     }
 
 }
-void MlWsumModel::unpack(MultidimArray<double> &packed)
+void MlWsumModel::unpack(MultidimArray<DOUBLE> &packed)
 {
     int spectral_size = (ori_size / 2) + 1;
 
@@ -959,7 +973,7 @@ void MlWsumModel::unpack(MultidimArray<double> &packed)
 }
 
 
-void MlWsumModel::pack(MultidimArray<double> &packed, int &piece, int &nr_pieces, bool do_clear)
+void MlWsumModel::pack(MultidimArray<DOUBLE> &packed, int &piece, int &nr_pieces, bool do_clear)
 {
 
 
@@ -999,7 +1013,7 @@ void MlWsumModel::pack(MultidimArray<double> &packed, int &piece, int &nr_pieces
     {
         idx_start = piece * MAX_PACK_SIZE;
         idx_stop = XMIPP_MIN(idx_start + MAX_PACK_SIZE, packed_size);
-        nr_pieces = CEIL((double)packed_size/(double)MAX_PACK_SIZE);
+        nr_pieces = CEIL((DOUBLE)packed_size/(DOUBLE)MAX_PACK_SIZE);
     }
     else
     {
@@ -1126,7 +1140,7 @@ void MlWsumModel::pack(MultidimArray<double> &packed, int &piece, int &nr_pieces
 
 }
 
-void MlWsumModel::unpack(MultidimArray<double> &packed, int piece, bool do_clear)
+void MlWsumModel::unpack(MultidimArray<DOUBLE> &packed, int piece, bool do_clear)
 {
 
 
diff --git a/src/ml_model.h b/src/ml_model.h
index c232484..9d55748 100644
--- a/src/ml_model.h
+++ b/src/ml_model.h
@@ -37,17 +37,20 @@ public:
 	// Dimension of the references (2D or 3D)
 	int ref_dim;
 
+	// Dimension of the data (2D or 3D)
+	int data_dim;
+
 	// Original size of the images
 	int ori_size;
 
 	// Pixel size (in Angstrom)
-	double pixel_size;
+	DOUBLE pixel_size;
 
 	// Current size of the images to be used in the expectation
 	int current_size;
 
 	// Current resolution (in 1/Ang)
-	double current_resolution;
+	DOUBLE current_resolution;
 
 	// Number of classes
 	int nr_classes;
@@ -62,7 +65,7 @@ public:
 	int nr_directions;
 
 	// Log-likelihood target value
-	double LL;
+	DOUBLE LL;
 
 	// Padding factor
 	int padding_factor;
@@ -74,19 +77,19 @@ public:
 	int r_min_nn;
 
 	// Average Pmax of the normalised probability distributions
-	double ave_Pmax;
+	DOUBLE ave_Pmax;
 
 	// Average normalisation correction factor
-	double avg_norm_correction;
+	DOUBLE avg_norm_correction;
 
 	// Variance in the origin offsets
-	double sigma2_offset;
+	DOUBLE sigma2_offset;
 
 	// Fudge factor to adjust estimated tau2_class spectra
-	double tau2_fudge_factor;
+	DOUBLE tau2_fudge_factor;
 
 	// Vector with all reference images
-	std::vector<MultidimArray<double> > Iref;
+	std::vector<MultidimArray<DOUBLE> > Iref;
 
 	// One projector for each class;
 	std::vector<Projector > PPref;
@@ -95,55 +98,55 @@ public:
 	std::vector<FileName> group_names;
 
 	// One noise spectrum for each group
-	std::vector<MultidimArray<double > > sigma2_noise;
+	std::vector<MultidimArray<DOUBLE > > sigma2_noise;
 
 	// One intensity scale for each group
-	std::vector<double> scale_correction;
+	std::vector<DOUBLE> scale_correction;
 
 	// One intensity B-factor for each group
-	std::vector<double> bfactor_correction;
+	std::vector<DOUBLE> bfactor_correction;
 
 	// Prior information: one restrained power_class spectrum for each class (inverse of right-hand side in Wiener-filter-like update formula)
-	std::vector<MultidimArray<double > > tau2_class;
+	std::vector<MultidimArray<DOUBLE > > tau2_class;
 
 	// Radial average of the estimated variance in the reconstruction (inverse of left-hand side in Wiener-filter-like update formula)
-	std::vector<MultidimArray<double > > sigma2_class;
+	std::vector<MultidimArray<DOUBLE > > sigma2_class;
 
 	// FSC spectra between random halves of the data
-	std::vector<MultidimArray<double > > fsc_halves_class;
+	std::vector<MultidimArray<DOUBLE > > fsc_halves_class;
 
 	// One likelihood vs prior ratio spectrum for each class
-	std::vector<MultidimArray<double > > data_vs_prior_class;
+	std::vector<MultidimArray<DOUBLE > > data_vs_prior_class;
 
 	// One value for each class
-	std::vector<double > pdf_class;
+	std::vector<DOUBLE > pdf_class;
 
 	// One array for each class
-	std::vector<MultidimArray<double> > pdf_direction;
+	std::vector<MultidimArray<DOUBLE> > pdf_direction;
 
 	// Priors for offsets for each class (only in 2D)
-	std::vector<Matrix1D<double> > prior_offset_class;
+	std::vector<Matrix1D<DOUBLE> > prior_offset_class;
 
 	// Mode for orientational prior distributions
 	int orientational_prior_mode;
 
 	// Variance in rot angle for the orientational pdf
-	double sigma2_rot;
+	DOUBLE sigma2_rot;
 
 	// Variance in tilt angle for the orientational pdf
-	double sigma2_tilt;
+	DOUBLE sigma2_tilt;
 
 	// Variance in psi angle for the orientational pdf
-	double sigma2_psi;
+	DOUBLE sigma2_psi;
 
 	// Estimated accuracy at which rotations can be assigned, one for each class
-	std::vector<double> acc_rot;
+	std::vector<DOUBLE> acc_rot;
 
 	// Estimated accuracy at which translations can be assigned, one for each class
-	std::vector<double> acc_trans;
+	std::vector<DOUBLE> acc_trans;
 
 	// Spectral contribution to orientability of individual particles, one for each class
-	std::vector<MultidimArray<double > > orientability_contrib;
+	std::vector<MultidimArray<DOUBLE > > orientability_contrib;
 
 
 public:
@@ -191,7 +194,7 @@ public:
 	void read(FileName fn_in);
 
 	// Write a model to disc
-	void write(FileName fn_out, HealpixSampling &sampling);
+	void write(FileName fn_out, HealpixSampling &sampling, bool do_write_bild = true);
 
 	//Read a tau-spectrum from a STAR file
 	void readTauSpectrum(FileName fn_tau, int verb);
@@ -206,11 +209,11 @@ public:
 	// For that: remove "00000i@" as well as movie extension from the rlnGroupName in the expanded Experiment and compare with group_names in current MlModel
 	void expandToMovieFrames(Experiment &moviedataexpand, int running_avg_side);
 
-	double getResolution(int ipix)	{ return (double)ipix/(pixel_size * ori_size); }
+	DOUBLE getResolution(int ipix)	{ return (DOUBLE)ipix/(pixel_size * ori_size); }
 
-	double getResolutionAngstrom(int ipix)	{ return (ipix==0) ? 999. : (pixel_size * ori_size)/(double)ipix; }
+	DOUBLE getResolutionAngstrom(int ipix)	{ return (ipix==0) ? 999. : (pixel_size * ori_size)/(DOUBLE)ipix; }
 
-	int getPixelFromResolution(double resol)	{ return (int)(resol * pixel_size * ori_size); }
+	int getPixelFromResolution(DOUBLE resol)	{ return (int)(resol * pixel_size * ori_size); }
 
 	/** Initialise pdf_orient arrays to the given size
 	* If the pdf_orient vectors were empty, resize them to the given size and initialise with an even distribution
@@ -237,14 +240,14 @@ public:
 
 	// Store the sum of the weights inside each group
 	// That is the number of particles inside each group
-	std::vector<double> sumw_group;
+	std::vector<DOUBLE> sumw_group;
 
 	// For the refinement of group intensity scales and bfactors
 	// For each group store weighted sums of experimental image times reference image as a function of resolution
-	std::vector<MultidimArray<double > > wsum_signal_product_spectra;
+	std::vector<MultidimArray<DOUBLE > > wsum_signal_product_spectra;
 
 	// For each group store weighted sums of squared reference as a function of resolution
-	std::vector<MultidimArray<double > > wsum_reference_power_spectra;
+	std::vector<MultidimArray<DOUBLE > > wsum_reference_power_spectra;
 
 	// Constructor
 	MlWsumModel()
@@ -272,21 +275,21 @@ public:
 	// Initialize all weighted sums to zero (with resizing the BPrefs to current_size)
 	void initZeros();
 
-	// Pack entire structure into one large MultidimArray<double> for reading/writing to disc
+	// Pack entire structure into one large MultidimArray<DOUBLE> for reading/writing to disc
 	// To save memory, the model itself will be cleared after packing.
-	void pack(MultidimArray<double> &packed);
+	void pack(MultidimArray<DOUBLE> &packed);
 
 	// Fill the model again using unpack (this is the inverse operation from pack)
-	void unpack(MultidimArray<double> &packed);
+	void unpack(MultidimArray<DOUBLE> &packed);
 
-	// Pack entire structure into one large MultidimArray<double> for shipping over with MPI
+	// Pack entire structure into one large MultidimArray<DOUBLE> for shipping over with MPI
 	// To save memory, the model itself will be cleared after packing.
     // If the whole thing becomes bigger than 1Gb (see MAX_PACK_SIZE in ml_model.cpp), then break it up into pieces because MPI cannot handle very large messages
 	// When broken up: nr_pieces > 1
-	void pack(MultidimArray<double> &packed, int &piece, int &nr_pieces, bool do_clear=true);
+	void pack(MultidimArray<DOUBLE> &packed, int &piece, int &nr_pieces, bool do_clear=true);
 
 	// Fill the model again using unpack (this is the inverse operation from pack)
-	void unpack(MultidimArray<double> &packed, int piece, bool do_clear=true);
+	void unpack(MultidimArray<DOUBLE> &packed, int piece, bool do_clear=true);
 
 };
 
diff --git a/src/ml_optimiser.cpp b/src/ml_optimiser.cpp
index f49f3a0..38fecbc 100644
--- a/src/ml_optimiser.cpp
+++ b/src/ml_optimiser.cpp
@@ -20,37 +20,20 @@
 #include "src/ml_optimiser.h"
 //#define DEBUG
 //#define DEBUG_CHECKSIZES
-//#define CHECKSIZES
+
+#define NR_CLASS_MUTEXES 5
+
 //Some global threads management variables
-Mutex global_mutex, global_mutex2;
+static pthread_mutex_t global_mutex2[NR_CLASS_MUTEXES] = { PTHREAD_MUTEX_INITIALIZER };
+static pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER;
 Barrier * global_barrier;
 ThreadManager * global_ThreadManager;
 
+/** ========================== Threaded parallelization of expectation === */
 
-// Global functions to work with threads
-void globalGetFourierTransformsAndCtfs(ThreadArgument &thArg)
-{
-	((MlOptimiser*)thArg.workClass)->doThreadGetFourierTransformsAndCtfs(thArg.thread_id);
-}
-
-void globalThreadPrecalculateShiftedImagesCtfsAndInvSigma2s(ThreadArgument &thArg)
-{
-	((MlOptimiser*)thArg.workClass)->doThreadPrecalculateShiftedImagesCtfsAndInvSigma2s(thArg.thread_id);
-}
-
-void globalThreadGetSquaredDifferencesAllOrientations(ThreadArgument &thArg)
-{
-	((MlOptimiser*)thArg.workClass)->doThreadGetSquaredDifferencesAllOrientations(thArg.thread_id);
-}
-
-void globalThreadConvertSquaredDifferencesToWeightsAllOrientations(ThreadArgument &thArg)
-{
-	((MlOptimiser*)thArg.workClass)->doThreadConvertSquaredDifferencesToWeightsAllOrientations(thArg.thread_id);
-}
-
-void globalThreadStoreWeightedSumsAllOrientations(ThreadArgument &thArg)
+void globalThreadExpectationSomeParticles(ThreadArgument &thArg)
 {
-	((MlOptimiser*)thArg.workClass)->doThreadStoreWeightedSumsAllOrientations(thArg.thread_id);
+	((MlOptimiser*)thArg.workClass)->doThreadExpectationSomeParticles(thArg.thread_id);
 }
 
 
@@ -71,6 +54,9 @@ void MlOptimiser::read(int argc, char **argv, int rank)
 
 	if (checkParameter(argc, argv, "--continue"))
 	{
+		// Do this before reading in the data.star file below!
+		do_preread_images   = checkParameter(argc, argv, "--preread_images");
+
 		parser.addSection("Continue options");
 		FileName fn_in = parser.getOption("--continue", "_optimiser.star file of the iteration after which to continue");
 		// Read in previously calculated parameters
@@ -190,7 +176,7 @@ void MlOptimiser::parseContinue(int argc, char **argv)
 		autosampling_hporder_local_searches = textToInteger(fnt);
 
 	// Check whether the prior mode changes
-	double _sigma_rot, _sigma_tilt, _sigma_psi, _sigma_off;
+	DOUBLE _sigma_rot, _sigma_tilt, _sigma_psi, _sigma_off;
 	int _mode;
 	fnt = parser.getOption("--sigma_ang", "Stddev on all three Euler angles for local angular searches (of +/- 3 stddev)", "OLD");
 	if (fnt != "OLD")
@@ -246,11 +232,10 @@ void MlOptimiser::parseContinue(int argc, char **argv)
 
 	nr_threads = textToInteger(parser.getOption("--j", "Number of threads to run in parallel (only useful on multi-core machines)", "1"));
 
-	fnt = parser.getOption("--pool", "Number of images to be processed together", "OLD");
-	if (fnt != "OLD")
-		max_nr_pool = textToInteger(fnt);
+	do_parallel_disc_io = !parser.checkOption("--no_parallel_disc_io", "Do NOT let parallel (MPI) processes access the disc simultaneously (use this option with NFS)");
 
 	combine_weights_thru_disc = !parser.checkOption("--dont_combine_weights_via_disc", "Send the large arrays of summed weights through the MPI network, instead of writing large files to disc");
+	do_shifts_onthefly = parser.checkOption("--onthefly_shifts", "Calculate shifted images on-the-fly, do not store precalculated ones in memory");
 
 	verb = textToInteger(parser.getOption("--verb", "Verbosity (1=normal, 0=silent)", "1"));
 
@@ -344,10 +329,10 @@ void MlOptimiser::parseInitial(int argc, char **argv)
 	do_auto_refine = parser.checkOption("--auto_refine", "Perform 3D auto-refine procedure?");
 	autosampling_hporder_local_searches = textToInteger(parser.getOption("--auto_local_healpix_order", "Minimum healpix order (before oversampling) from which autosampling procedure will use local searches", "4"));
 	parser.setSection(orientations_section);
-	double _sigma_ang = textToFloat(parser.getOption("--sigma_ang", "Stddev on all three Euler angles for local angular searches (of +/- 3 stddev)", "-1"));
-	double _sigma_rot = textToFloat(parser.getOption("--sigma_rot", "Stddev on the first Euler angle for local angular searches (of +/- 3 stddev)", "-1"));
-	double _sigma_tilt = textToFloat(parser.getOption("--sigma_tilt", "Stddev on the second Euler angle for local angular searches (of +/- 3 stddev)", "-1"));
-	double _sigma_psi = textToFloat(parser.getOption("--sigma_psi", "Stddev on the in-plane angle for local angular searches (of +/- 3 stddev)", "-1"));
+	DOUBLE _sigma_ang = textToFloat(parser.getOption("--sigma_ang", "Stddev on all three Euler angles for local angular searches (of +/- 3 stddev)", "-1"));
+	DOUBLE _sigma_rot = textToFloat(parser.getOption("--sigma_rot", "Stddev on the first Euler angle for local angular searches (of +/- 3 stddev)", "-1"));
+	DOUBLE _sigma_tilt = textToFloat(parser.getOption("--sigma_tilt", "Stddev on the second Euler angle for local angular searches (of +/- 3 stddev)", "-1"));
+	DOUBLE _sigma_psi = textToFloat(parser.getOption("--sigma_psi", "Stddev on the in-plane angle for local angular searches (of +/- 3 stddev)", "-1"));
 	if (_sigma_ang > 0.)
 	{
 		mymodel.orientational_prior_mode = PRIOR_ROTTILT_PSI;
@@ -387,8 +372,10 @@ void MlOptimiser::parseInitial(int argc, char **argv)
 	int computation_section = parser.addSection("Computation");
 	nr_threads = textToInteger(parser.getOption("--j", "Number of threads to run in parallel (only useful on multi-core machines)", "1"));
 	available_memory = textToFloat(parser.getOption("--memory_per_thread", "Available RAM (in Gb) for each thread", "2"));
-	max_nr_pool = textToInteger(parser.getOption("--pool", "Number of images to be processed together", "8"));
 	combine_weights_thru_disc = !parser.checkOption("--dont_combine_weights_via_disc", "Send the large arrays of summed weights through the MPI network, instead of writing large files to disc");
+	do_shifts_onthefly = parser.checkOption("--onthefly_shifts", "Calculate shifted images on-the-fly, do not store precalculated ones in memory");
+	do_parallel_disc_io = !parser.checkOption("--no_parallel_disc_io", "Do NOT let parallel (MPI) processes access the disc simultaneously (use this option with NFS)");
+        do_preread_images  = parser.checkOption("--preread_images", "Use this to let the master process read all particles into memory. Be careful you have enough RAM for large data sets!");
 
 	// Expert options
 	int expert_section = parser.addSection("Expert options");
@@ -407,14 +394,6 @@ void MlOptimiser::parseInitial(int argc, char **argv)
 	do_print_symmetry_ops = parser.checkOption("--print_symmetry_ops", "Print all symmetry transformation matrices, and exit");
 	strict_highres_exp = textToFloat(parser.getOption("--strict_highres_exp", "Resolution limit (in Angstrom) to restrict probability calculations in the expectation step", "-1"));
 	dont_raise_norm_error = parser.checkOption("--dont_check_norm", "Skip the check whether the images are normalised correctly");
-
-
-
-	// TODO: read/write do_always_cc in optmiser.star file!!!
-	// SA-stuff
-	do_sim_anneal = parser.checkOption("--sim_anneal", "Perform simulated-annealing to improve overall convergence of random starting models?");
-	temp_ini = textToFloat(parser.getOption("--temp_ini", "Initial temperature (K) for simulated annealing", "1000"));
-	temp_fin = textToFloat(parser.getOption("--temp_fin", "Initial temperature (K) for simulated annealing", "1"));
 	do_always_cc  = parser.checkOption("--always_cc", "Perform CC-calculation in all iterations (useful for faster denovo model generation?)");
 
 	///////////////// Special stuff for first iteration (only accessible via CL, not through readSTAR ////////////////////
@@ -543,7 +522,7 @@ void MlOptimiser::read(FileName fn_in, int rank)
 		!MD.getValue(EMDL_OPTIMISER_REFS_ARE_CTF_CORRECTED, refs_are_ctf_corrected) ||
 		!MD.getValue(EMDL_OPTIMISER_FIX_SIGMA_NOISE, fix_sigma_noise) ||
 		!MD.getValue(EMDL_OPTIMISER_FIX_SIGMA_OFFSET, fix_sigma_offset) ||
-		!MD.getValue(EMDL_OPTIMISER_MAX_NR_POOL, max_nr_pool) ||
+		!MD.getValue(EMDL_OPTIMISER_MAX_NR_POOL, nr_pool) ||
 		!MD.getValue(EMDL_OPTIMISER_AVAILABLE_MEMORY, available_memory))
     	REPORT_ERROR("MlOptimiser::readStar: incorrect optimiser_general table");
 
@@ -565,7 +544,9 @@ void MlOptimiser::read(FileName fn_in, int rank)
     debug1 = debug2 = 0.;
 
     // Then read in sampling, mydata and mymodel stuff
-    mydata.read(fn_data);
+    // If do_preread_images: when not do_parallel_disc_io: only the master reads all images into RAM; otherwise: everyone reads in images into RAM
+    bool do_preread = (do_preread_images) ? (do_parallel_disc_io || rank == 0) : false;
+    mydata.read(fn_data, false, false, do_preread);
     if (do_split_random_halves)
     {
 		if (rank % 2 == 1)
@@ -685,7 +666,7 @@ void MlOptimiser::write(bool do_write_sampling, bool do_write_data, bool do_writ
 		MD.setValue(EMDL_OPTIMISER_REFS_ARE_CTF_CORRECTED, refs_are_ctf_corrected);
 		MD.setValue(EMDL_OPTIMISER_FIX_SIGMA_NOISE, fix_sigma_noise);
 		MD.setValue(EMDL_OPTIMISER_FIX_SIGMA_OFFSET, fix_sigma_offset);
-		MD.setValue(EMDL_OPTIMISER_MAX_NR_POOL, max_nr_pool);
+		MD.setValue(EMDL_OPTIMISER_MAX_NR_POOL, nr_pool);
 		MD.setValue(EMDL_OPTIMISER_AVAILABLE_MEMORY, available_memory);
 
 		MD.write(fh);
@@ -695,10 +676,11 @@ void MlOptimiser::write(bool do_write_sampling, bool do_write_data, bool do_writ
 	// Then write the mymodel to file
 	if (do_write_model)
 	{
+            bool do_write_bild = !(do_skip_align || do_skip_rotate);
 		if (do_split_random_halves && !do_join_random_halves)
-			mymodel.write(fn_root + "_half" + integerToString(random_subset), sampling);
+			mymodel.write(fn_root + "_half" + integerToString(random_subset), sampling, do_write_bild);
 		else
-			mymodel.write(fn_root, sampling);
+			mymodel.write(fn_root, sampling, do_write_bild);
 	}
 
 	// And write the mydata to file
@@ -727,7 +709,7 @@ void MlOptimiser::initialise()
 	{
 		// Read in sigma_noise spetrum from file DEVELOPMENTAL!!! FOR DEBUGGING ONLY....
 		MetaDataTable MDsigma;
-		double val;
+		DOUBLE val;
 		int idx;
 		MDsigma.read(fn_sigma);
 		FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDsigma)
@@ -748,7 +730,7 @@ void MlOptimiser::initialise()
 	}
 	else if (do_calculate_initial_sigma_noise || do_average_unaligned)
 	{
-		MultidimArray<double> Mavg;
+		MultidimArray<DOUBLE> Mavg;
 
 		// Calculate initial sigma noise model from power_class spectra of the individual images
 		calculateSumOfPowerSpectraAndAverageImage(Mavg);
@@ -757,7 +739,7 @@ void MlOptimiser::initialise()
 		setSigmaNoiseEstimatesAndSetAverageImage(Mavg);
 	}
 
-    // First low-pass filter the initial references
+	// First low-pass filter the initial references
 	if (iter == 0)
 		initialLowPassFilterReferences();
 
@@ -819,24 +801,53 @@ void MlOptimiser::initialiseGeneral(int rank)
 #ifdef TIMING
 	//DIFFF = timer.setNew("difff");
 	TIMING_EXP =           timer.setNew("expectation");
+	TIMING_EXP_METADATA =  timer.setNew(" - EXP: metadata shuffling");
+	TIMING_EXP_CHANGES =   timer.setNew(" - EXP: monitor changes hidden variables");
 	TIMING_MAX =           timer.setNew("maximization");
 	TIMING_RECONS =        timer.setNew("reconstruction");
 	TIMING_ESP =           timer.setNew("expectationSomeParticles");
-	TIMING_ESP_READ  =     timer.setNew(" - ESP: read");
-	TIMING_ESP_DIFF1 =     timer.setNew(" - ESP: getAllSquaredDifferences1");
-	TIMING_ESP_DIFF2 =     timer.setNew(" - ESP: getAllSquaredDifferences2");
-	TIMING_DIFF_PROJ =     timer.setNew(" -  - ESPdiff2: project");
-	TIMING_DIFF_SHIFT =    timer.setNew(" -  - ESPdiff2: shift");
-	TIMING_DIFF_DIFF2 =    timer.setNew(" -  - ESPdiff2: diff2");
-	TIMING_ESP_WEIGHT1 =   timer.setNew(" - ESP: convertDiff2ToWeights1");
-	TIMING_ESP_WEIGHT2 =   timer.setNew(" - ESP: convertDiff2ToWeights2");
-	TIMING_WEIGHT_EXP =    timer.setNew(" -  - ESPweight: exp");
-	TIMING_WEIGHT_SORT =   timer.setNew(" -  - ESPweight: sort");
-	TIMING_ESP_WSUM =      timer.setNew(" - ESP: storeWeightedSums");
-	TIMING_WSUM_PROJ =     timer.setNew("  - - ESPwsum: project");
-	TIMING_WSUM_DIFF2 =    timer.setNew(" -  - ESPwsum: diff2");
-	TIMING_WSUM_SUMSHIFT = timer.setNew(" -  - ESPwsum: shift");
-	TIMING_WSUM_BACKPROJ = timer.setNew(" -  - ESPwsum: backproject");
+	TIMING_ESP_THR =       timer.setNew("doThreadExpectationSomeParticles");
+	TIMING_ESP_ONEPART =   timer.setNew("expectationOneParticle (thr0)");
+	TIMING_ESP_ONEPARTN =  timer.setNew("expectationOneParticle (thrN)");
+	TIMING_ESP_INI=        timer.setNew(" - EOP: initialise memory");
+	TIMING_ESP_FT =        timer.setNew(" - EOP: getFourierTransforms");
+	TIMING_ESP_PREC1 =     timer.setNew(" - EOP: precalcShifts1");
+	TIMING_ESP_PREC2 =     timer.setNew(" - EOP: precalcShifts2");
+	TIMING_ESP_DIFF1 =     timer.setNew(" - EOP: getAllSquaredDifferences1");
+	TIMING_ESP_DIFF2 =     timer.setNew(" - EOP: getAllSquaredDifferences2");
+	TIMING_ESP_DIFF2_A =   timer.setNew(" - EOP: getD2: A");
+	TIMING_ESP_DIFF2_B =   timer.setNew(" - EOP: getD2: B");
+	TIMING_ESP_DIFF2_C =   timer.setNew(" - EOP: getD2: C");
+	TIMING_ESP_DIFF2_D =   timer.setNew(" - EOP: getD2: D");
+	TIMING_ESP_DIFF2_E =   timer.setNew(" - EOP: getD2: E");
+	TIMING_DIFF_PROJ =     timer.setNew(" -  - EOPdiff2: project");
+	TIMING_DIFF_SHIFT =    timer.setNew(" -  - EOPdiff2: shift");
+	TIMING_DIFF2_GETSHIFT =timer.setNew(" -  - EOPdiff2: get shifted img");
+	TIMING_DIFF_DIFF2 =    timer.setNew(" -  - EOPdiff2: diff2");
+	TIMING_ESP_WEIGHT1 =   timer.setNew(" - EOP: convertDiff2ToWeights1");
+	TIMING_ESP_WEIGHT2 =   timer.setNew(" - EOP: convertDiff2ToWeights2");
+	TIMING_WEIGHT_EXP =    timer.setNew(" -  - EOPweight: exp");
+	TIMING_WEIGHT_SORT =   timer.setNew(" -  - EOPweight: sort");
+	TIMING_ESP_WSUM =      timer.setNew(" - EOP: storeWeightedSums");
+	TIMING_ESP_PRECW =     timer.setNew(" -  - EOPwsum: precalcShiftsW");
+	TIMING_WSUM_PROJ =     timer.setNew(" -  - EOPwsum: project");
+	TIMING_WSUM_GETSHIFT = timer.setNew(" -  - EOPwsum: get shifted img");
+	TIMING_WSUM_DIFF2 =    timer.setNew(" -  - EOPwsum: diff2");
+	TIMING_WSUM_LOCALSUMS =timer.setNew(" -  - EOPwsum: localsums");
+	TIMING_WSUM_SUMSHIFT = timer.setNew(" -  - EOPwsum: shiftimg");
+	TIMING_WSUM_BACKPROJ = timer.setNew(" -  - EOPwsum: backproject");
+
+	TIMING_EXTRA1= timer.setNew(" -extra1");
+	TIMING_EXTRA2= timer.setNew(" -extra2");
+	TIMING_EXTRA3= timer.setNew(" -extra3");
+#endif
+
+#ifdef FLOAT_PRECISION
+        if (verb > 0)
+            std::cout << " Running in single precision. Runs might not be exactly reproducible." << std::endl;
+#else
+        if (verb > 0)
+            std::cout << " Running in double precision. " << std::endl;
 #endif
 
 	if (do_print_metadata_labels)
@@ -861,13 +872,14 @@ void MlOptimiser::initialiseGeneral(int rank)
 	if (parser.checkForErrors(verb))
 		REPORT_ERROR("Errors encountered on the command line (see above), exiting...");
 
-	nr_threads_original = nr_threads;
 	// If we are not continuing an old run, now read in the data and the reference images
 	if (iter == 0)
 	{
 
 		// Read in the experimental image metadata
-		mydata.read(fn_data);
+                // If do_preread_images: only the master reads all images into RAM when not do_parallel_disc_io; otherwise everyone reads images into RAM!
+		bool do_preread = (do_preread_images) ? (do_parallel_disc_io || rank == 0) : false;
+		mydata.read(fn_data, true, false, do_preread); // true means ignore original particle name
 
 		// Also get original size of the images to pass to mymodel.read()
 		int ori_size = -1;
@@ -878,7 +890,7 @@ void MlOptimiser::initialiseGeneral(int rank)
     			do_average_unaligned, do_generate_seeds, refs_are_ctf_corrected);
 
     	// Check consistency of EMDL_CTF_MAGNIFICATION and MEBL_CTF_DETECTOR_PIXEL_SIZE with mymodel.pixel_size
-    	double mag, dstep, first_angpix, my_angpix;
+    	DOUBLE mag, dstep, first_angpix, my_angpix;
     	bool has_magn = false;
     	if (mydata.MDimg.containsLabel(EMDL_CTF_MAGNIFICATION) && mydata.MDimg.containsLabel(EMDL_CTF_DETECTOR_PIXEL_SIZE))
     	{
@@ -893,7 +905,10 @@ void MlOptimiser::initialiseGeneral(int rank)
     				has_magn = true;
     			}
     			else if (ABS(first_angpix - my_angpix) > 0.01)
+    			{
+    				std::cerr << " first_angpix= " << first_angpix << " my_angpix= " << my_angpix << " mag= " << mag  << " dstep= " << dstep << std::endl;
     				REPORT_ERROR("MlOptimiser::initialiseGeneral: ERROR inconsistent magnification and detector pixel sizes in images in input STAR file");
+    			}
 			}
     	}
     	if (mydata.MDmic.containsLabel(EMDL_CTF_MAGNIFICATION) && mydata.MDmic.containsLabel(EMDL_CTF_DETECTOR_PIXEL_SIZE))
@@ -936,7 +951,7 @@ void MlOptimiser::initialiseGeneral(int rank)
 		current_changes_optimal_offsets = 999.;
 
 		// If we're realigning movie frames, then now read in the metadata of the movie frames and combine with the metadata of the average images
-		mydata.expandToMovieFrames(fn_data_movie);
+		mydata.expandToMovieFrames(fn_data_movie, verb);
 
 		// Now also modify the model to contain many more groups....
 		// each groups has to become Nframes groups (get Nframes from new mydata)
@@ -964,9 +979,22 @@ void MlOptimiser::initialiseGeneral(int rank)
 		mymodel.readTauSpectrum(fn_tau, verb);
 	}
 
+	if (do_auto_refine)
+	{
+		nr_iter = 999;
+		has_fine_enough_angular_sampling = false;
+
+		if (iter == 0 && sampling.healpix_order >= autosampling_hporder_local_searches)
+		{
+			mymodel.orientational_prior_mode = PRIOR_ROTTILT_PSI;
+			sampling.orientational_prior_mode = PRIOR_ROTTILT_PSI;
+			DOUBLE rottilt_step = sampling.getAngularSampling(adaptive_oversampling);
+			mymodel.sigma2_rot = mymodel.sigma2_tilt = mymodel.sigma2_psi = 2. * 2. * rottilt_step * rottilt_step;
+		}
+	}
 
 	// Initialise the sampling object (sets prior mode and fills translations and rotations inside sampling object)
-	sampling.initialise(mymodel.orientational_prior_mode, mymodel.ref_dim, false);
+	sampling.initialise(mymodel.orientational_prior_mode, mymodel.ref_dim, mymodel.data_dim == 3);
 
 	// Default max_coarse_size is original size
 	if (max_coarse_size < 0)
@@ -975,12 +1003,6 @@ void MlOptimiser::initialiseGeneral(int rank)
 	if (particle_diameter < 0.)
     	particle_diameter = (mymodel.ori_size - width_mask_edge) * mymodel.pixel_size;
 
-	if (do_auto_refine)
-	{
-		nr_iter = 999;
-		has_fine_enough_angular_sampling = false;
-	}
-
     // For do_average_unaligned, always use initial low_pass filter
     if (do_average_unaligned && ini_high < 0.)
     {
@@ -993,44 +1015,33 @@ void MlOptimiser::initialiseGeneral(int rank)
     tab_sin.initialise(5000);
     tab_cos.initialise(5000);
 
-	// For skipped alignments: set nr_pool to one to have each thread work on one particle (with its own unique sampling arrays of 1 orientation and translation)
+	// For skipped alignments
 	// Also do not perturb this orientation, nor do oversampling or priors
-	if (do_skip_align || do_skip_rotate)
+	// Switch off on-the-fly shifts, as that will not work when skipping alignments! (it isn't necessary anyway in that case)
+    if (do_skip_align || do_skip_rotate)
 	{
 		mymodel.orientational_prior_mode = NOPRIOR;
 		sampling.orientational_prior_mode = NOPRIOR;
 		adaptive_oversampling = 0;
-		nr_pool = max_nr_pool = 1;
 		sampling.perturbation_factor = 0.;
 		sampling.random_perturbation = 0.;
-		sampling.setOneOrientation(0.,0.,0.);
+		sampling.addOneOrientation(0.,0.,0., true);
 		directions_have_changed = true;
-		if (do_realign_movies)
-			nr_threads = 1; // use only one thread, as there are no particles/orientations to parallelise anyway...
 		if (do_skip_align)
 		{
-			Matrix1D<double> offset(2);
-			sampling.setOneTranslation(offset);
+			DOUBLE dummy=0.;
+			sampling.addOneTranslation(dummy, dummy, dummy, true);
+			do_shifts_onthefly = false; // on-the-fly shifts are incompatible with do_skip_align!
 		}
 	}
 
 	// Resize the pdf_direction arrays to the correct size and fill with an even distribution
 	if (directions_have_changed)
-		mymodel.initialisePdfDirection(sampling.NrDirections(0, true));
+		mymodel.initialisePdfDirection(sampling.NrDirections());
 
 	// Initialise the wsum_model according to the mymodel
 	wsum_model.initialise(mymodel, sampling.symmetryGroup());
 
-	// Check that number of pooled particles is not larger than 1 for local angular searches
-	// Because for local searches, each particle has a different set of nonzeroprior orientations, and thus a differently sized Mweight
-	// If larger than 1, just reset to 1
-	if (mymodel.orientational_prior_mode != NOPRIOR && max_nr_pool > 1)
-	{
-		if (verb > 0)
-			std::cout << " Performing local angular searches! Lowering max_nr_pool from "<<max_nr_pool<<" to 1!" << std::endl;
-		max_nr_pool = 1;
-	}
-
 	// Initialise sums of hidden variable changes
 	// In later iterations, this will be done in updateOverallChangesInHiddenVariables
 	sum_changes_optimal_orientations = 0.;
@@ -1038,6 +1049,18 @@ void MlOptimiser::initialiseGeneral(int rank)
 	sum_changes_optimal_classes = 0.;
 	sum_changes_count = 0.;
 
+	if (mymodel.data_dim == 3)
+	{
+		// TODO: later do norm correction?!
+		// Don't do norm correction for volume averaging at this stage....
+		do_norm_correction = false;
+		do_shifts_onthefly = true; // save RAM for volume data (storing all shifted versions would take a lot!)
+		if (do_skip_align)
+			do_shifts_onthefly = false; // on-the-fly shifts are incompatible with do_skip_align!
+		// getMetaAndImageData is not made for passing multiple volumes!
+		do_parallel_disc_io = true;
+	}
+
 	// Skip scale correction if there are nor groups
 	if (mymodel.nr_groups == 1)
 		do_scale_correction = false;
@@ -1047,6 +1070,9 @@ void MlOptimiser::initialiseGeneral(int rank)
 	if (do_use_reconstruct_images && verb > 0)
 		std::cout <<" Using rlnReconstructImageName from the input data.star file!" << std::endl;
 
+	// For new thread-parallelization: each thread does 1 particle, so nr_pool=nr_threads
+	nr_pool = nr_threads;
+
 #ifdef DEBUG
 	std::cerr << "Leaving initialiseGeneral" << std::endl;
 #endif
@@ -1069,7 +1095,7 @@ void MlOptimiser::initialiseWorkLoad()
 
 }
 
-void MlOptimiser::calculateSumOfPowerSpectraAndAverageImage(MultidimArray<double> &Mavg, bool myverb)
+void MlOptimiser::calculateSumOfPowerSpectraAndAverageImage(MultidimArray<DOUBLE> &Mavg, bool myverb)
 {
 
 #ifdef DEBUG_INI
@@ -1086,12 +1112,11 @@ void MlOptimiser::calculateSumOfPowerSpectraAndAverageImage(MultidimArray<double
 
 	// Note the loop over the particles (part_id) is MPI-parallelized
 	int nr_ori_particles_done = 0;
-	Image<double> img;
+	Image<DOUBLE> img;
 	FileName fn_img;
-	MultidimArray<double> ind_spectrum, sum_spectrum, count;
+	MultidimArray<DOUBLE> ind_spectrum, sum_spectrum, count;
 	// For spectrum calculation: recycle the transformer (so do not call getSpectrum all the time)
 	MultidimArray<Complex > Faux;
-    Matrix1D<double> f(3);
     FourierTransformer transformer;
 	MetaDataTable MDimg;
 
@@ -1102,93 +1127,112 @@ void MlOptimiser::calculateSumOfPowerSpectraAndAverageImage(MultidimArray<double
 		{
 			long int part_id = mydata.ori_particles[ori_part_id].particles_id[i];
 
-			for (int iseries = 0; iseries < mydata.getNrImagesInSeries(part_id); iseries++)
+			long int group_id = mydata.getGroupId(part_id);
+			// TMP test for debuging
+			if (group_id < 0 || group_id >= mymodel.nr_groups)
 			{
+				std::cerr << " group_id= " << group_id << std::endl;
+				REPORT_ERROR("MlOptimiser::calculateSumOfPowerSpectraAndAverageImage: bad group_id");
+			}
 
-				long int group_id = mydata.getGroupId(part_id, iseries);
-				// TMP test for debuging
-				if (group_id < 0 || group_id >= mymodel.nr_groups)
+			// Extract the relevant MetaDataTable row from MDimg
+			MDimg = mydata.getMetaDataImage(part_id);
+
+			// Get the image filename
+			MDimg.getValue(EMDL_IMAGE_NAME, fn_img);
+
+			// Read image from disc
+                        if (do_preread_images && do_parallel_disc_io)
+                        {
+                            img() = mydata.particles[part_id].img;
+                        }
+                        else
+                        {
+                            img.read(fn_img);
+                            img().setXmippOrigin();
+                        }
+
+			// Check that the average in the noise area is approximately zero and the stddev is one
+			if (!dont_raise_norm_error)
+			{
+				int bg_radius2 = ROUND(particle_diameter / (2. * mymodel.pixel_size));
+				bg_radius2 *= bg_radius2;
+				DOUBLE sum = 0.;
+				DOUBLE sum2 = 0.;
+				DOUBLE nn = 0.;
+				FOR_ALL_ELEMENTS_IN_ARRAY3D(img())
 				{
-					std::cerr << " group_id= " << group_id << std::endl;
-					REPORT_ERROR("MlOptimiser::calculateSumOfPowerSpectraAndAverageImage: bad group_id");
+					if (k*k+i*i+j*j > bg_radius2)
+					{
+						sum += A3D_ELEM(img(), k, i, j);
+						sum2 += A3D_ELEM(img(), k, i, j) * A3D_ELEM(img(), k, i, j);
+						nn += 1.;
+					}
+				}
+				// stddev
+				sum2 -= sum*sum/nn;
+				sum2 = sqrt(sum2/nn);
+				//average
+				sum /= nn;
+
+				// Average should be close to zero, i.e. max +/-50% of stddev...
+				// Stddev should be close to one, i.e. larger than 0.5 and smaller than 2)
+				if (ABS(sum/sum2) > 0.5 || sum2 < 0.5 || sum2 > 2.0)
+				{
+					std::cerr << " fn_img= " << fn_img << " bg_avg= " << sum << " bg_stddev= " << sum2 << std::endl;
+					REPORT_ERROR("ERROR: It appears that these images have not been normalised to an average background value of 0 and a stddev value of 1. \n \
+							Note that the average and stddev values for the background are calculated outside a circle with the particle diameter \n \
+							You can use the relion_preprocess program to normalise your images \n \
+							If you are sure you have normalised the images correctly (also see the RELION Wiki), you can switch off this error message using the --dont_check_norm command line option");
 				}
+			}
 
-				// Extract the relevant MetaDataTable row from MDimg
-				MDimg = mydata.getMetaDataImage(part_id, iseries);
+			// Apply a similar softMask as below (assume zero translations)
+			if (do_zero_mask)
+				softMaskOutsideMap(img(), particle_diameter / (2. * mymodel.pixel_size), width_mask_edge);
 
-				// Get the image filename
-				MDimg.getValue(EMDL_IMAGE_NAME, fn_img);
+			// Randomize the initial orientations for volume refinements
+			if (mymodel.data_dim == 3)
+			{
+				DOUBLE rot, tilt, psi;
+				Matrix2D<DOUBLE> A;
+				rot = rnd_unif()*360.;
+				tilt = rnd_unif()*180.;
+				psi = rnd_unif()*360.;
+				Euler_angles2matrix(rot, tilt, psi, A, true);
+				DOUBLE stddev1 = img().computeStddev();
+				selfApplyGeometry(img(), A, IS_INV, WRAP);
+				DOUBLE stddev2 = img().computeStddev();
+				// Correct for interpolation errors that drive down the average density...
+				img() *= stddev1 / stddev2;
+			}
 
-				// Read image from disc
-				img.read(fn_img);
-				img().setXmippOrigin();
+			// Calculate this image's power spectrum in: ind_spectrum
+			ind_spectrum.initZeros(XSIZE(img()));
+			count.initZeros(XSIZE(img()));
+			// recycle the same transformer for all images
+			transformer.FourierTransform(img(), Faux, false);
+			FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(Faux)
+			{
+				long int idx = ROUND(sqrt(kp*kp + ip*ip + jp*jp));
+				ind_spectrum(idx) += norm(dAkij(Faux, k, i, j));
+				count(idx) += 1.;
+			}
+			ind_spectrum /= count;
 
-				// Check that the average in the noise area is approximately zero and the stddev is one
-				if (!dont_raise_norm_error)
-				{
-					int bg_radius2 = ROUND(particle_diameter / (2. * mymodel.pixel_size));
-					bg_radius2 *= bg_radius2;
-					double sum = 0.;
-					double sum2 = 0.;
-					double nn = 0.;
-					FOR_ALL_ELEMENTS_IN_ARRAY3D(img())
-					{
-						if (k*k+i*i+j*j > bg_radius2)
-						{
-							sum += A3D_ELEM(img(), k, i, j);
-							sum2 += A3D_ELEM(img(), k, i, j) * A3D_ELEM(img(), k, i, j);
-							nn += 1.;
-						}
-					}
-					// stddev
-					sum2 -= sum*sum/nn;
-					sum2 = sqrt(sum2/nn);
-					//average
-					sum /= nn;
-
-					// Average should be close to zero, i.e. max +/-50% of stddev...
-					// Stddev should be close to one, i.e. larger than 0.5 and smaller than 2)
-					if (ABS(sum/sum2) > 0.5 || sum2 < 0.5 || sum2 > 2.0)
-					{
-						std::cerr << " fn_img= " << fn_img << " bg_avg= " << sum << " bg_stddev= " << sum2 << std::endl;
-						REPORT_ERROR("ERROR: It appears that these images have not been normalised to an average background value of 0 and a stddev value of 1. \n \
-								Note that the average and stddev values for the background are calculated outside a circle with the particle diameter \n \
-								You can use the relion_preprocess program to normalise your images \n \
-								If you are sure you have normalised the images correctly (also see the RELION Wiki), you can switch off this error message using the --dont_check_norm command line option");
-					}
-				}
+			// Resize the power_class spectrum to the correct size and keep sum
+			ind_spectrum.resize(wsum_model.sigma2_noise[0]); // Store sum of all groups in group 0
+			wsum_model.sigma2_noise[0] += ind_spectrum;
+			wsum_model.sumw_group[0] += 1.;
+			mymodel.nr_particles_group[group_id] += 1;
 
-				// Apply a similar softMask as below (assume zero translations)
-				if (do_zero_mask)
-					softMaskOutsideMap(img(), particle_diameter / (2. * mymodel.pixel_size), width_mask_edge);
-
-				// Calculate this image's power spectrum in: ind_spectrum
-				ind_spectrum.initZeros(XSIZE(img()));
-			    count.initZeros(XSIZE(img()));
-			    // recycle the same transformer for all images
-			    transformer.FourierTransform(img(), Faux, false);
-			    FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(Faux)
-			    {
-			    	long int idx = ROUND(sqrt(kp*kp + ip*ip + jp*jp));
-			    	ind_spectrum(idx) += norm(dAkij(Faux, k, i, j));
-			        count(idx) += 1.;
-			    }
-			    ind_spectrum /= count;
-
-				// Resize the power_class spectrum to the correct size and keep sum
-				ind_spectrum.resize(wsum_model.sigma2_noise[0]); // Store sum of all groups in group 0
-				wsum_model.sigma2_noise[0] += ind_spectrum;
-				wsum_model.sumw_group[0] += 1.;
-				mymodel.nr_particles_group[group_id] += 1;
-
-
-				// Also calculate average image
-				if (part_id == mydata.ori_particles[my_first_ori_particle_id].particles_id[0])
-					Mavg = img();
-				else
-					Mavg += img();
 
-			} // end loop iseries
+			// Also calculate average image
+			if (part_id == mydata.ori_particles[my_first_ori_particle_id].particles_id[0])
+				Mavg = img();
+			else
+				Mavg += img();
+
 		} // end loop part_id (i)
 
 		if (myverb > 0 && nr_ori_particles_done % barstep == 0)
@@ -1210,7 +1254,7 @@ void MlOptimiser::calculateSumOfPowerSpectraAndAverageImage(MultidimArray<double
 
 }
 
-void MlOptimiser::setSigmaNoiseEstimatesAndSetAverageImage(MultidimArray<double> &Mavg)
+void MlOptimiser::setSigmaNoiseEstimatesAndSetAverageImage(MultidimArray<DOUBLE> &Mavg)
 {
 
 #ifdef DEBUG_INI
@@ -1234,7 +1278,7 @@ void MlOptimiser::setSigmaNoiseEstimatesAndSetAverageImage(MultidimArray<double>
 		mymodel.sigma2_noise[0] = wsum_model.sigma2_noise[0] / ( 2. * wsum_model.sumw_group[0] );
 
 		// Calculate power spectrum of the average image
-		MultidimArray<double> spect;
+		MultidimArray<DOUBLE> spect;
 		getSpectrum(Mavg, spect, POWER_SPECTRUM);
 		spect /= 2.; // because of 2-dimensionality of the complex plane
 
@@ -1247,7 +1291,6 @@ void MlOptimiser::setSigmaNoiseEstimatesAndSetAverageImage(MultidimArray<double>
 			mymodel.sigma2_noise[igroup] = mymodel.sigma2_noise[0];
 	}
 
-
 #ifdef DEBUG_INI
     std::cerr<<"MlOptimiser::setSigmaNoiseEstimatesAndSetAverageImage Leaving"<<std::endl;
 #endif
@@ -1261,9 +1304,9 @@ void MlOptimiser::initialLowPassFilterReferences()
 
 		// Make a soft (raised cosine) filter in Fourier space to prevent artefacts in real-space
 		// The raised cosine goes through 0.5 at the filter frequency and has a width of width_mask_edge fourier pixels
-		double radius = mymodel.ori_size * mymodel.pixel_size / ini_high;
+		DOUBLE radius = mymodel.ori_size * mymodel.pixel_size / ini_high;
 		radius -= WIDTH_FMASK_EDGE / 2.;
-		double radius_p = radius + WIDTH_FMASK_EDGE;
+		DOUBLE radius_p = radius + WIDTH_FMASK_EDGE;
 		FourierTransformer transformer;
 		MultidimArray<Complex > Faux;
 		for (int iclass = 0; iclass < mymodel.nr_classes; iclass++)
@@ -1271,7 +1314,7 @@ void MlOptimiser::initialLowPassFilterReferences()
 			transformer.FourierTransform(mymodel.Iref[iclass], Faux);
 			FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(Faux)
 			{
-				double r = sqrt((double)(kp*kp + ip*ip + jp*jp));
+				DOUBLE r = sqrt((DOUBLE)(kp*kp + ip*ip + jp*jp));
 				if (r < radius)
 					continue;
 				else if (r > radius_p)
@@ -1300,8 +1343,7 @@ void MlOptimiser::iterateSetup()
 	global_ThreadManager = new ThreadManager(nr_threads, this);
 
 	// Set up the thread task distributors for the particles and the orientations (will be resized later on)
-	exp_ipart_ThreadTaskDistributor = new ThreadTaskDistributor(1, 1);
-	exp_iorient_ThreadTaskDistributor = new ThreadTaskDistributor(1, 1);
+	exp_ipart_ThreadTaskDistributor = new ThreadTaskDistributor(nr_threads, 1);
 
 }
 void MlOptimiser::iterateWrapUp()
@@ -1310,7 +1352,6 @@ void MlOptimiser::iterateWrapUp()
 	// delete barrier, threads and task distributors
     delete global_barrier;
 	delete global_ThreadManager;
-    delete exp_iorient_ThreadTaskDistributor;
     delete exp_ipart_ThreadTaskDistributor;
 
 }
@@ -1338,14 +1379,6 @@ void MlOptimiser::iterate()
 		timer.tic(TIMING_EXP);
 #endif
 
-		// SA-stuff
-		if (do_sim_anneal)
-		{
-			double tau = -nr_iter / (std::log(temp_fin/temp_ini));
-			temperature = temp_ini * exp(-iter/tau);
-			std::cout << " temperature= " << temperature << std::endl;
-		}
-
 		if (do_auto_refine)
 			printConvergenceStats();
 
@@ -1430,7 +1463,7 @@ void MlOptimiser::expectation()
 	// A. Update current size (may have been changed to ori_size in autoAdjustAngularSampling) and resolution pointers
 	updateImageSizeAndResolutionPointers();
 
-	// B. Initialise Fouriertransform, set weights in wsum_model to zero, etc
+	// B. Initialise Fouriertransform, set weights in wsum_model to zero, initialise AB-matrices for FFT-phase shifts, etc
 	expectationSetup();
 
 #ifdef DEBUG_EXP
@@ -1443,7 +1476,7 @@ void MlOptimiser::expectation()
 	if (!((iter==1 && do_firstiter_cc) || do_always_cc) && !do_skip_align)
 	{
 		// Set the exp_metadata (but not the exp_imagedata which is not needed for calculateExpectedAngularErrors)
-		int n_trials_acc = (mymodel.ref_dim==3) ? 100 : 10;
+		int n_trials_acc = (mymodel.ref_dim==3 && mymodel.data_dim != 3) ? 100 : 10;
 		n_trials_acc = XMIPP_MIN(n_trials_acc, mydata.numberOfOriginalParticles());
 		getMetaAndImageDataSubset(0, n_trials_acc-1, false);
 		calculateExpectedAngularErrors(0, n_trials_acc-1);
@@ -1453,9 +1486,14 @@ void MlOptimiser::expectation()
 	if ( iter > 1 && (do_auto_refine) )
 		updateAngularSampling();
 
-	// E. Check whether everything fits into memory, possibly adjust nr_pool and setup thread task managers
+	// E. Check whether everything fits into memory
 	expectationSetupCheckMemory();
 
+	// F. Precalculate AB-matrices for on-the-fly shifts
+	if (do_shifts_onthefly)
+		precalculateABMatrices();
+
+
 #ifdef DEBUG_EXP
 	std::cerr << "Expectation: done setupCheckMemory" << std::endl;
 #endif
@@ -1477,21 +1515,43 @@ void MlOptimiser::expectation()
 	while (nr_ori_particles_done < mydata.numberOfOriginalParticles())
 	{
 
+#ifdef TIMING
+		timer.tic(TIMING_EXP_METADATA);
+#endif
+
 		my_first_ori_particle = nr_ori_particles_done;
 		my_last_ori_particle = XMIPP_MIN(mydata.numberOfOriginalParticles() - 1, my_first_ori_particle + nr_pool - 1);
 
 		// Get the metadata for these particles
-		getMetaAndImageDataSubset(my_first_ori_particle, my_last_ori_particle);
+		getMetaAndImageDataSubset(my_first_ori_particle, my_last_ori_particle, !do_parallel_disc_io);
+
+#ifdef TIMING
+		timer.toc(TIMING_EXP_METADATA);
+#endif
 
 		// perform the actual expectation step on several particles
 		expectationSomeParticles(my_first_ori_particle, my_last_ori_particle);
 
+#ifdef TIMING
+		timer.tic(TIMING_EXP_METADATA);
+#endif
+
 		// Set the metadata for these particles
 		setMetaDataSubset(my_first_ori_particle, my_last_ori_particle);
 
+
+#ifdef TIMING
+		timer.toc(TIMING_EXP_METADATA);
+		timer.tic(TIMING_EXP_CHANGES);
+#endif
+
 		// Also monitor the changes in the optimal orientations and classes
 		monitorHiddenVariableChanges(my_first_ori_particle, my_last_ori_particle);
 
+#ifdef TIMING
+		timer.toc(TIMING_EXP_CHANGES);
+#endif
+
 		nr_ori_particles_done += my_last_ori_particle - my_first_ori_particle + 1;
 
 		if (verb > 0 && nr_ori_particles_done - prev_barstep > barstep)
@@ -1538,20 +1598,22 @@ void MlOptimiser::expectationSetup()
 void MlOptimiser::expectationSetupCheckMemory(bool myverb)
 {
 
+	std::vector<int> pointer_dir_nonzeroprior, pointer_psi_nonzeroprior;
+	std::vector<DOUBLE> directions_prior, psi_prior;
 	if (mymodel.orientational_prior_mode != NOPRIOR)
 	{
 		// First select one random direction and psi-angle for selectOrientationsWithNonZeroPriorProbability
 		// This is to get an idea how many non-zero probabilities there will be
-		double ran_rot, ran_tilt, ran_psi;
-		int randir = (int)(rnd_unif() * sampling.NrDirections(0, true) );
-		int ranpsi = (int)(rnd_unif() * sampling.NrPsiSamplings(0, true) );
-		if (randir == sampling.NrDirections(0, true))
+		DOUBLE ran_rot, ran_tilt, ran_psi;
+		int randir = (int)(rnd_unif() * sampling.NrDirections() );
+		int ranpsi = (int)(rnd_unif() * sampling.NrPsiSamplings() );
+		if (randir == sampling.NrDirections())
 		{
 			//TMP
 			REPORT_ERROR("RANDIR WAS TOO BIG!!!!");
 			randir--;
 		}
-		if (ranpsi == sampling.NrPsiSamplings(0, true))
+		if (ranpsi == sampling.NrPsiSamplings())
 		{
 			//TMP
 			REPORT_ERROR("RANPSI WAS TOO BIG!!!!");
@@ -1561,104 +1623,53 @@ void MlOptimiser::expectationSetupCheckMemory(bool myverb)
 		sampling.getPsiAngle(ranpsi, ran_psi);
 		// Calculate local searches for these angles
 		sampling.selectOrientationsWithNonZeroPriorProbability(ran_rot, ran_tilt, ran_psi,
-								sqrt(mymodel.sigma2_rot), sqrt(mymodel.sigma2_tilt), sqrt(mymodel.sigma2_psi));
+								sqrt(mymodel.sigma2_rot), sqrt(mymodel.sigma2_tilt), sqrt(mymodel.sigma2_psi),
+								pointer_dir_nonzeroprior, directions_prior, pointer_psi_nonzeroprior, psi_prior);
 	}
 
 	// Check whether things will fit into memory
-	// Each double takes 8 bytes, and their are mymodel.nr_classes references, express in Gb
-	double Gb = sizeof(double) / (1024. * 1024. * 1024.);
+	// Each DOUBLE takes 8 bytes, and their are mymodel.nr_classes references, express in Gb
+	DOUBLE Gb = sizeof(DOUBLE) / (1024. * 1024. * 1024.);
 	// A. Calculate approximate size of the reference maps
 	// Forward projector has complex data, backprojector has complex data and real weight
-	double mem_references = Gb * mymodel.nr_classes * (2 * MULTIDIM_SIZE((mymodel.PPref[0]).data) + 3 * MULTIDIM_SIZE((wsum_model.BPref[0]).data));
-	// B. Calculate size of the exp_Mweight matrices with (YSIZE=nr_pool, XSIZE=mymodel.nr_classes * sampling.NrSamplingPoints(adaptive_oversampling)
-	nr_pool = max_nr_pool;
-
-	if (mydata.maxNumberOfImagesPerOriginalParticle() > 1)
+	DOUBLE mem_references = Gb * mymodel.nr_classes * (2 * MULTIDIM_SIZE((mymodel.PPref[0]).data) + 3 * MULTIDIM_SIZE((wsum_model.BPref[0]).data));
+	// B. Weight vectors
+	DOUBLE mem_pool = Gb * mymodel.nr_classes * sampling.NrSamplingPoints(adaptive_oversampling,
+			&pointer_dir_nonzeroprior, &pointer_psi_nonzeroprior);
+	// C. The original image data
+	int nr_pix = (mymodel.data_dim == 2) ? mymodel.current_size * mymodel.current_size : mymodel.current_size * mymodel.current_size * mymodel.current_size;
+	mem_pool += Gb * nr_pix;
+	if (!do_shifts_onthefly)
 	{
-		// Make sure that all particles in the data set have the same number of images
-		// with the same transformation matrices so that their exp_R_mic can be re-used for pooled particles
-		// If there are some particles with different transformations, then just set nr_pool to one
-		// TODO: optimize this for randomised particle order.....
-		// Currently that will lead to pretty bad efficiency IF there are multiple different tilt angles....
-		// Or perhaps just forget about pooling. If we're re-refining the orientations that will be screwed anyway...
-
-		// First find a particle with the maxNumberOfImagesPerParticle
-		long int ref_part;
-		long int maxn = mydata.maxNumberOfImagesPerOriginalParticle();
-		for (ref_part = 0; ref_part < mydata.numberOfParticles(); ref_part++)
-		{
-			if (mydata.getNrImagesInSeries(ref_part) == maxn)
-				break;
-		}
-
-		// Then check the transformation matrices for all the other particles are all the same
-		// Note that particles are allowed to have fewer images in their series...
-		Matrix2D<double> first_R_mic, test_R_mic;
-		bool is_ok = true;
-		for (long int ipart = 0; ipart < mydata.numberOfParticles(); ipart++)
-		{
-			for (int iseries = 0; iseries < mydata.getNrImagesInSeries(ipart); iseries++)
-			{
-				first_R_mic = mydata.getMicrographTransformationMatrix(ref_part, iseries);
-				test_R_mic = mydata.getMicrographTransformationMatrix(ipart, iseries);
-				if (!first_R_mic.equal(test_R_mic))
-				{
-					is_ok = false;
-					break;
-				}
-			}
-		}
-		if (!is_ok)
-		{
-			// Don't pool particles to prevent trouble when re-using exp_R_mic...
-			nr_pool = 1;
-			if (myverb > 0)
-				std::cout << " Switching off the pooling of particles because there are some series with distinct transformation matrices present in the data... ";
-		}
+		// D. All precalculated shifted images as well (both masked and unmasked)
+		mem_pool += Gb * nr_pix * 2 * sampling.NrTranslationalSamplings(adaptive_oversampling);
 	}
-
-	double mem_pool = Gb * nr_pool * mymodel.nr_classes * sampling.NrSamplingPoints(adaptive_oversampling, false);
 	// Estimate the rest of the program at 0.1 Gb?
-	double mem_rest = 0.1;
-	double total_mem_Gb_exp = mem_references + mem_pool + mem_rest;
-	// Each reconstruction has to store 1 extra complex array (Fconv) and 4 extra double arrays (Fweight, Fnewweight. vol_out and Mconv in convoluteBlobRealSpace),
-	// in adddition to the double weight-array and the complex data-array of the BPref
-	// That makes a total of 2*2 + 5 = 9 * a double array of size BPref
-	double total_mem_Gb_max = Gb * 9 * MULTIDIM_SIZE((wsum_model.BPref[0]).data);
-
-	bool exp_does_not_fit = false;
-	if (total_mem_Gb_exp > available_memory * nr_threads_original)
+	DOUBLE mem_rest = 0.1; // This one does NOT scale with nr_pool
+	if (do_shifts_onthefly)
 	{
-		double mem_for_pool = (available_memory * nr_threads_original) - mem_rest - mem_references;
-		int suggested_nr_pool = FLOOR(mem_for_pool / (Gb * mymodel.nr_classes * sampling.NrSamplingPoints(adaptive_oversampling, true)));
-		if (suggested_nr_pool > 0)
-		{
-			if (myverb > 0)
-			{
-				std::cout << "Reducing nr_pool to "<< suggested_nr_pool<<" to still fit into memory" << std::endl;
-			}
-			nr_pool = suggested_nr_pool;
-			mem_pool = Gb * nr_pool * mymodel.nr_classes * sampling.NrSamplingPoints(adaptive_oversampling, false);
-			total_mem_Gb_exp = mem_references + mem_pool + mem_rest;
-		}
-		else
-		{
-			exp_does_not_fit = true;
-		}
+		// E. Store all AB-matrices
+		mem_rest += Gb * nr_pix * sampling.NrTranslationalSamplings(adaptive_oversampling);
 	}
 
+	DOUBLE total_mem_Gb_exp = mem_references + nr_pool * mem_pool + mem_rest;
+	// Each reconstruction has to store 1 extra complex array (Fconv) and 4 extra DOUBLE arrays (Fweight, Fnewweight. vol_out and Mconv in convoluteBlobRealSpace),
+	// in adddition to the DOUBLE weight-array and the complex data-array of the BPref
+	// That makes a total of 2*2 + 5 = 9 * a DOUBLE array of size BPref
+	DOUBLE total_mem_Gb_max = Gb * 9 * MULTIDIM_SIZE((wsum_model.BPref[0]).data);
+
 	if (myverb > 0)
 	{
 		// Calculate number of sampled hidden variables:
 		int nr_ang_steps = CEIL(PI * particle_diameter * mymodel.current_resolution);
-		double myresol_angstep = 360. / nr_ang_steps;
+		DOUBLE myresol_angstep = 360. / nr_ang_steps;
 		std::cout << " CurrentResolution= " << 1./mymodel.current_resolution << " Angstroms, which requires orientationSampling of at least "<< myresol_angstep
 				   <<" degrees for a particle of diameter "<< particle_diameter << " Angstroms"<< std::endl;
 		for (int oversampling = 0; oversampling <= adaptive_oversampling; oversampling++)
 		{
-			std::cout << " Oversampling= " << oversampling << " NrHiddenVariableSamplingPoints= " << mymodel.nr_classes * sampling.NrSamplingPoints(oversampling, true) << std::endl;
+			std::cout << " Oversampling= " << oversampling << " NrHiddenVariableSamplingPoints= " << mymodel.nr_classes * sampling.NrSamplingPoints(oversampling, &pointer_dir_nonzeroprior, &pointer_psi_nonzeroprior) << std::endl;
 			std::cout << " OrientationalSampling= " << sampling.getAngularSampling(oversampling)
-				<< " NrOrientations= "<<sampling.NrDirections(oversampling, false)*sampling.NrPsiSamplings(oversampling, false)<<std::endl;
+				<< " NrOrientations= "<<sampling.NrDirections(oversampling, &pointer_dir_nonzeroprior)*sampling.NrPsiSamplings(oversampling, &pointer_psi_nonzeroprior)<<std::endl;
 			std::cout << " TranslationalSampling= " << sampling.getTranslationalSampling(oversampling)
 				<< " NrTranslations= "<<sampling.NrTranslationalSamplings(oversampling)<< std::endl;
 			std::cout << "=============================" << std::endl;
@@ -1667,41 +1678,100 @@ void MlOptimiser::expectationSetupCheckMemory(bool myverb)
 
 	if (myverb > 0)
 	{
-		std::cout << " Estimated memory for expectation step  > " << total_mem_Gb_exp << " Gb, available memory = "<<available_memory * nr_threads_original<<" Gb."<<std::endl;
-		std::cout << " Estimated memory for maximization step > " << total_mem_Gb_max << " Gb, available memory = "<<available_memory * nr_threads_original<<" Gb."<<std::endl;
+		std::cout << " Estimated memory for expectation step  > " << total_mem_Gb_exp << " Gb, available memory = "<<available_memory * nr_threads<<" Gb."<<std::endl;
+		std::cout << " Estimated memory for maximization step > " << total_mem_Gb_max << " Gb, available memory = "<<available_memory * nr_threads<<" Gb."<<std::endl;
 
-		if (total_mem_Gb_max > available_memory * nr_threads_original || exp_does_not_fit)
+		if (total_mem_Gb_max > available_memory * nr_threads || total_mem_Gb_exp > available_memory * nr_threads)
 		{
-			if (exp_does_not_fit)
-			std::cout << " WARNING!!! Expected to run out of memory during expectation step ...." << std::endl;
-			if (total_mem_Gb_max > available_memory * nr_threads_original)
-			std::cout << " WARNING!!! Expected to run out of memory during maximization step ...." << std::endl;
-			std::cout << " WARNING!!! Did you set --memory_per_thread to reflect the number of Gb per core on your computer?" << std::endl;
-			std::cout << " WARNING!!! If so, then check your processes are not swapping and consider running fewer MPI processors per node." << std::endl;
+			std::cout << " WARNING!!! Did you set --memory_per_thread to reflect the true Gb per core on your computer?" << std::endl;
+			if (total_mem_Gb_exp > available_memory * nr_threads)
+			{
+				std::cout << " WARNING!!! Expected to run out of memory during expectation step ...." << std::endl;
+				std::cout << " WARNING!!! Check your processes are not swapping ... " << std::endl;
+				if (!do_shifts_onthefly)
+					std::cout << " WARNING!!! Consider not using --precalculate_shifts !! " << std::endl;
+			}
+			if (total_mem_Gb_max > available_memory * nr_threads)
+			{
+				std::cout << " WARNING!!! Expected to run out of memory during maximization step ...." << std::endl;
+				std::cout << " WARNING!!! Check your processes are not swapping ... " << std::endl;
+				std::cout << " WARNING!!! Consider running fewer MPI processors per node." << std::endl;
+			}
 			std::cout << " + Available memory for each thread, as given by --memory_per_thread      : " << available_memory << " Gb" << std::endl;
-			std::cout << " + Number of threads used per MPI process, as given by --j                : " << nr_threads_original << std::endl;
-			std::cout << " + Available memory per MPI process 										: " << available_memory * nr_threads_original << " Gb" << std::endl;
+			std::cout << " + Number of threads used per MPI process, as given by --j                : " << nr_threads << std::endl;
+			std::cout << " + Available memory per MPI process 										: " << available_memory * nr_threads << " Gb" << std::endl;
 		}
 	}
 
-	// Now that we also have nr_pool, resize the task manager for the particles
+#ifdef DEBUG
+	std::cerr << "Leaving expectationSetup" << std::endl;
+#endif
+
+}
 
-	/// When there are multiple particles for each ori_particle, then this ThreadTaskDistributor will again be resized somewhere below
-	exp_ipart_ThreadTaskDistributor->resize(nr_pool, 1);
+void MlOptimiser::precalculateABMatrices()
+{
 
-	// Also resize task manager for the orientations in case of NOPRIOR (otherwise resizing is done in doThreadGetFourierTransformsAndCtfs)
-	if (do_skip_align || do_skip_rotate)
-	{
-		exp_iorient_ThreadTaskDistributor->resize(1, 1);
-	}
-	else if (mymodel.orientational_prior_mode == NOPRIOR)
+	// Set the global AB-matrices for the FFT phase-shifted images
+	global_fftshifts_ab_coarse.clear();
+	global_fftshifts_ab_current.clear();
+	global_fftshifts_ab2_coarse.clear();
+	global_fftshifts_ab2_current.clear();
+	MultidimArray<Complex> Fab_current, Fab_coarse;
+	if (mymodel.data_dim == 3)
+		Fab_current.resize(mymodel.current_size, mymodel.current_size, mymodel.current_size / 2 + 1);
+	else
+		Fab_current.resize(mymodel.current_size, mymodel.current_size / 2 + 1);
+	long int exp_nr_trans = sampling.NrTranslationalSamplings();
+	std::vector<DOUBLE> oversampled_translations_x, oversampled_translations_y, oversampled_translations_z;
+	// Note that do_shifts_onthefly is incompatible with do_skip_align because of the loop below
+	for (long int itrans = 0; itrans < exp_nr_trans; itrans++)
 	{
-		long int nr_orients = sampling.NrDirections() * sampling.NrPsiSamplings();
-		int threadBlockSize = (nr_orients > 100) ? 10 : 1;
-		exp_iorient_ThreadTaskDistributor->resize(nr_orients, threadBlockSize);
-	}
-#ifdef DEBUG
-	std::cerr << "Leaving expectationSetup" << std::endl;
+		// First get the non-oversampled translations as defined by the sampling object
+		sampling.getTranslations(itrans, 0, oversampled_translations_x,
+				oversampled_translations_y, oversampled_translations_z); // need getTranslations to add random_perturbation
+		if (mymodel.data_dim == 2)
+			getAbMatricesForShiftImageInFourierTransform(Fab_current, Fab_current, tab_sin, tab_cos,
+				(DOUBLE)mymodel.ori_size, oversampled_translations_x[0], oversampled_translations_y[0]);
+		else
+			getAbMatricesForShiftImageInFourierTransform(Fab_current, Fab_current, tab_sin, tab_cos,
+				(DOUBLE)mymodel.ori_size, oversampled_translations_x[0], oversampled_translations_y[0], oversampled_translations_z[0]);
+
+		windowFourierTransform(Fab_current, Fab_coarse, coarse_size);
+		global_fftshifts_ab_coarse.push_back(Fab_coarse);
+		if (adaptive_oversampling == 0)
+		{
+			global_fftshifts_ab_current.push_back(Fab_current);
+		}
+		else
+		{
+			// Then also loop over all its oversampled relatives
+			// Then loop over all its oversampled relatives
+			sampling.getTranslations(itrans, 1, oversampled_translations_x, oversampled_translations_y, oversampled_translations_z);
+			for (long int iover_trans = 0; iover_trans < oversampled_translations_x.size(); iover_trans++)
+			{
+				// Shift through phase-shifts in the Fourier transform
+				// Note that the shift search range is centered around (exp_old_xoff, exp_old_yoff)
+				if (mymodel.data_dim == 2)
+					getAbMatricesForShiftImageInFourierTransform(Fab_current, Fab_current, tab_sin, tab_cos,
+						(DOUBLE)mymodel.ori_size, oversampled_translations_x[iover_trans], oversampled_translations_y[iover_trans]);
+				else
+					getAbMatricesForShiftImageInFourierTransform(Fab_current, Fab_current, tab_sin, tab_cos,
+						(DOUBLE)mymodel.ori_size, oversampled_translations_x[iover_trans], oversampled_translations_y[iover_trans], oversampled_translations_z[iover_trans]);
+
+				global_fftshifts_ab2_current.push_back(Fab_current);
+				if (strict_highres_exp > 0.)
+				{
+					windowFourierTransform(Fab_current, Fab_coarse, coarse_size);
+					global_fftshifts_ab2_coarse.push_back(Fab_coarse);
+				}
+			}
+		} // end else (adaptive_oversampling == 0)
+	} // end loop itrans
+
+#ifdef DEBUG_AB
+	std::cerr << " global_fftshifts_ab_coarse.size()= " << global_fftshifts_ab_coarse.size() << " global_fftshifts_ab_current.size()= " << global_fftshifts_ab_current.size() << std::endl;
+	std::cerr << " global_fftshifts_ab2_coarse.size()= " << global_fftshifts_ab2_coarse.size() << " global_fftshifts_ab2_current.size()= " << global_fftshifts_ab2_current.size() << std::endl;
 #endif
 
 }
@@ -1713,50 +1783,147 @@ void MlOptimiser::expectationSomeParticles(long int my_first_ori_particle, long
 	timer.tic(TIMING_ESP);
 #endif
 
-//#define DEBUG_EXPSINGLE
-#ifdef DEBUG_EXPSINGLE
+//#define DEBUG_EXPSOME
+#ifdef DEBUG_EXPSOME
 	std::cerr << "Entering expectationSomeParticles..." << std::endl;
 #endif
 
+    // Use global variables for thread visibility (before there were local ones for similar call in MPI version!)
+	exp_my_first_ori_particle = my_first_ori_particle;
+    exp_my_last_ori_particle = my_last_ori_particle;
+
+    // Store total number of particle images in this bunch of SomeParticles, and set translations and orientations for skip_align/rotate
+    exp_nr_images = 0;
+    for (long int ori_part_id = my_first_ori_particle; ori_part_id <= my_last_ori_particle; ori_part_id++)
+	{
+
+		// If skipping alignment (not for movies) or rotations (for movies, but all frames (part_id) have the same rotation)
+		// then store the old translation and orientation for each ori_particle (take first part_id!)
+		// If we do local angular searches, get the previously assigned angles to center the prior
+		if (do_skip_align || do_skip_rotate)
+		{
+			bool do_clear = (ori_part_id == my_first_ori_particle);
+			if (do_skip_align)
+			{
+				// Rounded translations will be applied to the image upon reading,
+				// set the unique translation in the sampling object to the fractional difference
+				DOUBLE my_old_offset_x, my_old_offset_y, my_old_offset_z;
+				DOUBLE rounded_offset_x, rounded_offset_y, rounded_offset_z;
+				my_old_offset_x = DIRECT_A2D_ELEM(exp_metadata, exp_nr_images, METADATA_XOFF);
+				my_old_offset_y = DIRECT_A2D_ELEM(exp_metadata, exp_nr_images, METADATA_YOFF);
+				rounded_offset_x = my_old_offset_x - ROUND(my_old_offset_x);
+				rounded_offset_y = my_old_offset_y - ROUND(my_old_offset_y);
+				if (mymodel.data_dim == 3)
+				{
+					my_old_offset_z = DIRECT_A2D_ELEM(exp_metadata, exp_nr_images, METADATA_ZOFF);
+					rounded_offset_z = my_old_offset_z - ROUND(my_old_offset_z);
+				}
+				sampling.addOneTranslation(rounded_offset_x, rounded_offset_y, rounded_offset_z, do_clear); // clear for first ori_particle
+			}
+			// Also set the rotations
+			DOUBLE old_rot, old_tilt, old_psi;
+			old_rot = DIRECT_A2D_ELEM(exp_metadata, exp_nr_images, METADATA_ROT);
+			old_tilt = DIRECT_A2D_ELEM(exp_metadata, exp_nr_images, METADATA_TILT);
+			old_psi = DIRECT_A2D_ELEM(exp_metadata, exp_nr_images, METADATA_PSI);
+			sampling.addOneOrientation(old_rot, old_tilt, old_psi, do_clear);
+		}
+
+		// Store total number of particle images in this bunch of SomeParticles
+		exp_nr_images += mydata.ori_particles[ori_part_id].particles_id.size();
+
+	}
+
+#ifdef DEBUG_EXPSOME
+	std::cerr << " exp_my_first_ori_particle= " << exp_my_first_ori_particle << " exp_my_last_ori_particle= " << exp_my_last_ori_particle << std::endl;
+	std::cerr << " exp_nr_images= " << exp_nr_images << std::endl;
+#endif
+
+	exp_ipart_ThreadTaskDistributor->resize(my_last_ori_particle - my_first_ori_particle + 1, 1);
+	exp_ipart_ThreadTaskDistributor->reset();
+    global_ThreadManager->run(globalThreadExpectationSomeParticles);
+
 #ifdef TIMING
-    timer.tic(TIMING_ESP);
-    timer.tic(TIMING_ESP_READ);
+    timer.toc(TIMING_ESP);
 #endif
 
-    // Use global variables for thread visibility
-	exp_my_first_ori_particle = my_first_ori_particle;
-    exp_my_last_ori_particle = my_last_ori_particle;
-    exp_nr_ori_particles = exp_my_last_ori_particle - exp_my_first_ori_particle + 1;
 
-    // Find out how many particles there are in these ori_particles
-    exp_nr_particles = 0;
-    for (long int i = my_first_ori_particle; i <= my_last_ori_particle; i++)
-    	exp_nr_particles += mydata.ori_particles[i].particles_id.size();
+}
 
-    // If there are more than one particle in each ori_particle, then do these in parallel with threads
-    if (nr_pool == 1 && exp_nr_particles/exp_nr_ori_particles > 1)
-    {
-    	int my_pool = exp_nr_particles/exp_nr_ori_particles;
-    	exp_ipart_ThreadTaskDistributor->resize(my_pool, 1);
-    }
 
-    // TODO: MAKE SURE THAT ALL PARTICLES IN SomeParticles ARE FROM THE SAME AREA, SO THAT THE R_mic CAN BE RE_USED!!!
+void MlOptimiser::doThreadExpectationSomeParticles(int thread_id)
+{
+
+#ifdef TIMING
+	// Only time one thread
+	if (thread_id == 0)
+		timer.tic(TIMING_ESP_THR);
+#endif
+
+	size_t first_ipart = 0, last_ipart = 0;
+	while (exp_ipart_ThreadTaskDistributor->getTasks(first_ipart, last_ipart))
+	{
+//#define DEBUG_EXPSOMETHR
+#ifdef DEBUG_EXPSOMETHR
+		pthread_mutex_lock(&global_mutex);
+		std::cerr << " thread_id= " << thread_id << " first_ipart= " << first_ipart << " last_ipart= " << last_ipart << std::endl;
+		std::cerr << " exp_my_first_ori_particle= " << exp_my_first_ori_particle << " exp_my_last_ori_particle= " << exp_my_last_ori_particle << std::endl;
+		pthread_mutex_unlock(&global_mutex);
+#endif
+
+		for (long int ipart = first_ipart; ipart <= last_ipart; ipart++)
+		{
+#ifdef TIMING
+			// Only time one thread
+			if (thread_id == 0)
+				timer.tic(TIMING_ESP_ONEPART);
+			else if (thread_id == nr_threads -1)
+				timer.tic(TIMING_ESP_ONEPARTN);
+#endif
+
+			expectationOneParticle(exp_my_first_ori_particle + ipart, thread_id);
+
+#ifdef TIMING
+			// Only time one thread
+			if (thread_id == 0)
+				timer.toc(TIMING_ESP_ONEPART);
+			else if (thread_id == nr_threads -1)
+				timer.toc(TIMING_ESP_ONEPARTN);
+#endif
+
+		}
+	}
+
+#ifdef TIMING
+	// Only time one thread
+	if (thread_id == 0)
+		timer.toc(TIMING_ESP_THR);
+#endif
+
+}
+
+
+void MlOptimiser::expectationOneParticle(long int my_ori_particle, int thread_id)
+{
 
-	// In the first iteration, multiple seeds will be generated
+#ifdef TIMING
+	if (my_ori_particle == exp_my_first_ori_particle)
+		timer.tic(TIMING_ESP_INI);
+#endif
+    // In the first iteration, multiple seeds will be generated
 	// A single random class is selected for each pool of images, and one does not marginalise over the orientations
 	// The optimal orientation is based on signal-product (rather than the signal-intensity sensitive Gaussian)
     // If do_firstiter_cc, then first perform a single iteration with K=1 and cross-correlation criteria, afterwards
 
-    // Generally: use all references
-    iclass_min = 0;
-    iclass_max = mymodel.nr_classes - 1;
+    // Decide which classes to integrate over (for random class assignment in 1st iteration)
+    int exp_iclass_min = 0;
+    int exp_iclass_max = mymodel.nr_classes - 1;
     // low-pass filter again and generate the seeds
     if (do_generate_seeds)
     {
     	if (do_firstiter_cc && iter == 1)
     	{
     		// In first (CC) iter, use a single reference (and CC)
-    		iclass_min = iclass_max = 0;
+    		exp_iclass_min = exp_iclass_max = 0;
     	}
     	else if ( (do_firstiter_cc && iter == 2) || (!do_firstiter_cc && iter == 1))
 		{
@@ -1764,52 +1931,126 @@ void MlOptimiser::expectationSomeParticles(long int my_first_ori_particle, long
     		// Now select a single random class
     		// exp_part_id is already in randomized order (controlled by -seed)
     		// WARNING: USING SAME iclass_min AND iclass_max FOR SomeParticles!!
-			iclass_min = iclass_max = divide_equally_which_group(mydata.numberOfOriginalParticles(), mymodel.nr_classes, exp_my_first_ori_particle);
+    		exp_iclass_min = exp_iclass_max = divide_equally_which_group(mydata.numberOfOriginalParticles(), mymodel.nr_classes, my_ori_particle);
 		}
     }
 
-	// TODO: think of a way to have the different images in a single series have DIFFERENT offsets!!!
-	// Right now, they are only centered with a fixed relative translation!!!!
-
-// Thid debug is a good one to step through the separate steps of the expectation to see where trouble lies....
+// This debug is a good one to step through the separate steps of the expectation to see where trouble lies....
 //#define DEBUG_ESP_MEM
 #ifdef DEBUG_ESP_MEM
-	char c;
-	std::cerr << "Before getFourierTransformsAndCtfs, press any key to continue... " << std::endl;
-	std::cin >> c;
-#endif
 
-	// Read all image of this series into memory, apply old origin offsets and store Fimg, Fctf, exp_old_xoff and exp_old_yoff in vectors./
+	std::cerr << "Entering MlOptimiser::expectationOneParticle" << std::endl;
+    std::cerr << " my_ori_particle= " << my_ori_particle << std::endl;
+    std::cerr << " exp_iclass_min= " << exp_iclass_min << " exp_iclass_max= " << exp_iclass_max << std::endl;
+    std::cerr << " exp_idir_min= " << exp_idir_min << " exp_idir_max= " << exp_idir_max << std::endl;
+    std::cerr << " exp_ipsi_min= " << exp_ipsi_min << " exp_ipsi_max= " << exp_ipsi_max << std::endl;
+    std::cerr << " exp_itrans_min= " << exp_itrans_min << " exp_itrans_max= " << exp_itrans_max << std::endl;
+    if (thread_id==0)
+	{
+		char c;
+		std::cerr << "Before getFourierTransformsAndCtfs, press any key to continue... " << std::endl;
+		std::cin >> c;
+	}
+    global_barrier->wait();
+#endif
+
+
+	// Here define all kind of arrays that will be needed
+	std::vector<MultidimArray<Complex > > exp_Fimgs, exp_Fimgs_nomask, exp_local_Fimgs_shifted, exp_local_Fimgs_shifted_nomask;
+	std::vector<MultidimArray<DOUBLE> > exp_Fctfs, exp_local_Fctfs, exp_local_Minvsigma2s;
+	std::vector<int> exp_pointer_dir_nonzeroprior, exp_pointer_psi_nonzeroprior;
+	std::vector<DOUBLE> exp_directions_prior, exp_psi_prior, exp_local_sqrtXi2;
+	int exp_current_image_size, exp_current_oversampling;
+	std::vector<DOUBLE> exp_highres_Xi2_imgs, exp_min_diff2;
+	MultidimArray<DOUBLE> exp_Mweight;
+	MultidimArray<bool> exp_Mcoarse_significant;
+	// And from storeWeightedSums
+	std::vector<DOUBLE> exp_sum_weight, exp_significant_weight, exp_max_weight;
+	std::vector<Matrix1D<DOUBLE> > exp_old_offset, exp_prior;
+	std::vector<DOUBLE> exp_wsum_norm_correction;
+	std::vector<MultidimArray<DOUBLE> > exp_wsum_scale_correction_XA, exp_wsum_scale_correction_AA, exp_power_imgs;
+	DOUBLE exp_thisparticle_sumweight;
+
+	int exp_nr_particles = mydata.ori_particles[my_ori_particle].particles_id.size();
+	// Global exp_metadata array has metadata of all ori_particles. Where does my_ori_particle start?
+	int metadata_offset = 0;
+	for (long int iori = exp_my_first_ori_particle; iori <= exp_my_last_ori_particle; iori++)
+	{
+		if (iori == my_ori_particle)
+			break;
+		metadata_offset += mydata.ori_particles[iori].particles_id.size();
+	}
+
+	// Resize vectors for all particles
+	exp_power_imgs.resize(exp_nr_particles);
+	exp_highres_Xi2_imgs.resize(exp_nr_particles);
+	exp_Fimgs.resize(exp_nr_particles);
+	exp_Fimgs_nomask.resize(exp_nr_particles);
+	exp_Fctfs.resize(exp_nr_particles);
+	exp_old_offset.resize(exp_nr_particles);
+	exp_prior.resize(exp_nr_particles);
 
-	exp_ipart_ThreadTaskDistributor->reset();
-	global_ThreadManager->run(globalGetFourierTransformsAndCtfs);
+	// Then calculate all Fourier Transforms
 
-	if (do_realign_movies )//&& movie_frame_running_avg_side > 0)
+#ifdef TIMING
+	if (my_ori_particle == exp_my_first_ori_particle)
 	{
-		calculateRunningAveragesOfMovieFrames();
+		timer.toc(TIMING_ESP_INI);
+		timer.tic(TIMING_ESP_FT);
 	}
+#endif
 
-#ifdef DEBUG_ESP_MEM
-	std::cerr << "After getFourierTransformsAndCtfs, press any key to continue... " << std::endl;
-	std::cin >> c;
+	getFourierTransformsAndCtfs(my_ori_particle, metadata_offset, exp_Fimgs, exp_Fimgs_nomask, exp_Fctfs,
+			exp_old_offset, exp_prior, exp_power_imgs, exp_highres_Xi2_imgs,
+			exp_pointer_dir_nonzeroprior, exp_pointer_psi_nonzeroprior, exp_directions_prior, exp_psi_prior);
+
+#ifdef TIMING
+	if (my_ori_particle == exp_my_first_ori_particle)
+		timer.toc(TIMING_ESP_FT);
 #endif
 
-	#ifdef TIMING
-    timer.toc(TIMING_ESP_READ);
+#ifdef DEBUG_ESP_MEM
+	if (thread_id==0)
+	{
+		char c;
+		std::cerr << " exp_nr_particles= " << exp_nr_particles << " metadata_offset= " << metadata_offset << std::endl;
+		std::cerr << "After getFourierTransformsAndCtfs, press any key to continue... " << std::endl;
+		std::cin >> c;
+	}
+	global_barrier->wait();
 #endif
 
-	// Initialise significant weight to minus one, so that all coarse sampling points will be handled in the first pass
-	exp_significant_weight.clear();
-	exp_significant_weight.resize(exp_nr_particles);
-	for (int n = 0; n < exp_nr_particles; n++)
-		exp_significant_weight[n] = -1.;
+	if (do_realign_movies && movie_frame_running_avg_side > 0)
+	{
+		calculateRunningAveragesOfMovieFrames(my_ori_particle, exp_Fimgs, exp_power_imgs, exp_highres_Xi2_imgs);
+	}
 
-	// Number of rotational and translational sampling points
-	exp_nr_trans = sampling.NrTranslationalSamplings();
+	// To deal with skipped alignments/rotations
+	int exp_itrans_min, exp_itrans_max, exp_idir_min, exp_idir_max, exp_ipsi_min, exp_ipsi_max;
+	if (do_skip_align)
+	{
+		exp_itrans_min = exp_itrans_max = exp_idir_min = exp_idir_max = exp_ipsi_min = exp_ipsi_max =
+				my_ori_particle - exp_my_first_ori_particle;
+	}
+	else
+	{
+		exp_itrans_min = 0;
+		exp_itrans_max = sampling.NrTranslationalSamplings() - 1;
+		if (do_skip_rotate)
+		{
+			exp_idir_min = exp_idir_max = exp_ipsi_min = exp_ipsi_max =
+					my_ori_particle - exp_my_first_ori_particle;
+		}
+		else
+		{
+			exp_idir_min = exp_ipsi_min = 0;
+			exp_idir_max = sampling.NrDirections(0, &exp_pointer_dir_nonzeroprior) - 1;
+			exp_ipsi_max = sampling.NrPsiSamplings(0, &exp_pointer_psi_nonzeroprior ) - 1;
+		}
+	}
 
-	exp_nr_dir = sampling.NrDirections();
-	exp_nr_psi = sampling.NrPsiSamplings();
-	exp_nr_rot = exp_nr_dir * exp_nr_psi;
+	// Initialise significant weight to minus one, so that all coarse sampling points will be handled in the first pass
+	exp_significant_weight.resize(exp_nr_particles, -1.);
 
 	// Only perform a second pass when using adaptive oversampling
 	int nr_sampling_passes = (adaptive_oversampling > 0) ? 2 : 1;
@@ -1818,7 +2059,7 @@ void MlOptimiser::expectationSomeParticles(long int my_first_ori_particle, long
 	// The first pass uses a coarser angular sampling and possibly smaller FFTs than the second pass.
 	// Only those sampling points that contribute to the highest x% of the weights in the first pass are oversampled in the second pass
 	// Only those sampling points will contribute to the weighted sums in the third loop below
-	for (exp_ipass = 0; exp_ipass < nr_sampling_passes; exp_ipass++)
+	for (int exp_ipass = 0; exp_ipass < nr_sampling_passes; exp_ipass++)
 	{
 
 		if (strict_highres_exp > 0.)
@@ -1832,59 +2073,91 @@ void MlOptimiser::expectationSomeParticles(long int my_first_ori_particle, long
 
 		// Use coarse sampling in the first pass, oversampled one the second pass
 		exp_current_oversampling = (exp_ipass == 0) ? 0 : adaptive_oversampling;
-		exp_nr_oversampled_rot = sampling.oversamplingFactorOrientations(exp_current_oversampling);
-		exp_nr_oversampled_trans = sampling.oversamplingFactorTranslations(exp_current_oversampling);
-
 
 #ifdef DEBUG_ESP_MEM
-
-	std::cerr << "Before getAllSquaredDifferences, use top to see memory usage and then press any key to continue... " << std::endl;
-	std::cin >> c;
+		if (thread_id==0)
+		{
+			char c;
+			std::cerr << " exp_current_image_size= " << exp_current_image_size << " exp_current_oversampling= " << exp_current_oversampling << " nr_sampling_passes= " << nr_sampling_passes << std::endl;
+			std::cerr << "Before getAllSquaredDifferences, use top to see memory usage and then press any key to continue... " << std::endl;
+			std::cin >> c;
+		}
+		global_barrier->wait();
 #endif
 
 		// Calculate the squared difference terms inside the Gaussian kernel for all hidden variables
-		getAllSquaredDifferences();
+		getAllSquaredDifferences(my_ori_particle, exp_current_image_size, exp_ipass, exp_current_oversampling,
+				metadata_offset, exp_idir_min, exp_idir_max, exp_ipsi_min, exp_ipsi_max,
+				exp_itrans_min, exp_itrans_max, exp_iclass_min, exp_iclass_max, exp_min_diff2, exp_highres_Xi2_imgs,
+				exp_Fimgs, exp_Fctfs, exp_Mweight, exp_Mcoarse_significant,
+				exp_pointer_dir_nonzeroprior, exp_pointer_psi_nonzeroprior, exp_directions_prior, exp_psi_prior,
+				exp_local_Fimgs_shifted, exp_local_Minvsigma2s, exp_local_Fctfs, exp_local_sqrtXi2);
 
 #ifdef DEBUG_ESP_MEM
-	std::cerr << "After getAllSquaredDifferences, use top to see memory usage and then press any key to continue... " << std::endl;
-	std::cin >> c;
+		if (thread_id==0)
+		{
+			char c;
+			std::cerr << "After getAllSquaredDifferences, use top to see memory usage and then press any key to continue... " << std::endl;
+			std::cin >> c;
+		}
+		global_barrier->wait();
 #endif
 
 		// Now convert the squared difference terms to weights,
 		// also calculate exp_sum_weight, and in case of adaptive oversampling also exp_significant_weight
-		convertAllSquaredDifferencesToWeights();
+		convertAllSquaredDifferencesToWeights(my_ori_particle, exp_ipass, exp_current_oversampling, metadata_offset,
+				exp_idir_min, exp_idir_max, exp_ipsi_min, exp_ipsi_max,
+				exp_itrans_min, exp_itrans_max, exp_iclass_min, exp_iclass_max,
+				exp_Mweight, exp_Mcoarse_significant, exp_significant_weight,
+				exp_sum_weight, exp_old_offset, exp_prior, exp_min_diff2,
+				exp_pointer_dir_nonzeroprior, exp_pointer_psi_nonzeroprior, exp_directions_prior, exp_psi_prior);
 
 #ifdef DEBUG_ESP_MEM
-	std::cerr << "After convertAllSquaredDifferencesToWeights, press any key to continue... " << std::endl;
-	std::cin >> c;
+	if (thread_id==0)
+	{
+		char c;
+		std::cerr << "After convertAllSquaredDifferencesToWeights, press any key to continue... " << std::endl;
+		std::cin >> c;
+	}
+	global_barrier->wait();
 #endif
 
 	}// end loop over 2 exp_ipass iterations
 
-
 	// For the reconstruction step use mymodel.current_size!
 	exp_current_image_size = mymodel.current_size;
 
 #ifdef DEBUG_ESP_MEM
-	std::cerr << "Before storeWeightedSums, press any key to continue... " << std::endl;
-	std::cin >> c;
+	if (thread_id==0)
+	{
+		char c;
+		std::cerr << "Before storeWeightedSums, press any key to continue... " << std::endl;
+		std::cin >> c;
+	}
+	global_barrier->wait();
 #endif
-	storeWeightedSums();
 
-	// Now calculate the optimal translation for each of the individual images in the series
-	//if (mydata.maxNumberOfImagesPerOriginalParticle(my_first_ori_particle, my_last_ori_particle) > 1 && !(do_firstiter_cc && iter == 1))
-	//	getOptimalOrientationsForIndividualImagesInSeries();
+	storeWeightedSums(my_ori_particle, exp_current_image_size, exp_current_oversampling, metadata_offset,
+			exp_idir_min, exp_idir_max, exp_ipsi_min, exp_ipsi_max,
+			exp_itrans_min, exp_itrans_max, exp_iclass_min, exp_iclass_max,
+			exp_min_diff2, exp_highres_Xi2_imgs, exp_Fimgs, exp_Fimgs_nomask, exp_Fctfs,
+			exp_power_imgs, exp_old_offset, exp_prior, exp_Mweight, exp_Mcoarse_significant,
+			exp_significant_weight, exp_sum_weight, exp_max_weight,
+			exp_pointer_dir_nonzeroprior, exp_pointer_psi_nonzeroprior, exp_directions_prior, exp_psi_prior,
+			exp_local_Fimgs_shifted, exp_local_Fimgs_shifted_nomask, exp_local_Minvsigma2s, exp_local_Fctfs, exp_local_sqrtXi2);
 
 #ifdef DEBUG_ESP_MEM
-	std::cerr << "After storeWeightedSums, press any key to continue... " << std::endl;
-	std::cin >> c;
-#endif
-#ifdef DEBUG_EXPSINGLE
-		std::cerr << "Leaving expectationSingleParticle..." << std::endl;
+	if (thread_id==0)
+	{
+		char c;
+		std::cerr << "After storeWeightedSums, press any key to continue... " << std::endl;
+		std::cin >> c;
+	}
+	global_barrier->wait();
 #endif
 
-#ifdef TIMING
-	timer.toc(TIMING_ESP);
+#ifdef DEBUG_EXPSINGLE
+		std::cerr << "Leaving expectationOneParticle..." << std::endl;
 #endif
 
 }
@@ -1924,10 +2197,6 @@ void MlOptimiser::maximization()
 	// Keep track of changes in hidden variables
 	updateOverallChangesInHiddenVariables();
 
-	// This doesn't really work, and I need the original priors for the polishing...
-	//if (do_realign_movies)
-	//	updatePriorsForMovieFrames();
-
 	if (verb > 0)
 		progress_bar(mymodel.nr_classes);
 
@@ -1941,7 +2210,7 @@ void MlOptimiser::maximizationOtherParameters()
 #endif
 
 	// Calculate total sum of weights, and average CTF for each class (for SSNR estimation)
-	double sum_weight = 0.;
+	DOUBLE sum_weight = 0.;
 	for (int iclass = 0; iclass < mymodel.nr_classes; iclass++)
 		sum_weight += wsum_model.pdf_class[iclass];
 
@@ -1953,30 +2222,34 @@ void MlOptimiser::maximizationOtherParameters()
 
 	if (do_scale_correction && !(iter==1 && do_firstiter_cc) )
 	{
-		double avg_scale_correction = 0., nr_part = 0.;
 		for (int igroup = 0; igroup < mymodel.nr_groups; igroup++)
 		{
-
-#ifdef DEVEL_BFAC
-			// TMP
-			if (verb>0)
-			{
-				for (int i=0; i<XSIZE(wsum_model.wsum_signal_product_spectra[igroup]); i++)
-				{
-					std::cout <<" igroup= "<<igroup<< " i= "<<i<<" "<<wsum_model.wsum_signal_product_spectra[igroup](i)<<" "<<wsum_model.wsum_reference_power_spectra[igroup](i)<<std::endl;
-				}
-			}
-#endif
-
-			double sumXA = wsum_model.wsum_signal_product_spectra[igroup].sum();
-			double sumAA = wsum_model.wsum_reference_power_spectra[igroup].sum();
+			DOUBLE sumXA = wsum_model.wsum_signal_product_spectra[igroup].sum();
+			DOUBLE sumAA = wsum_model.wsum_reference_power_spectra[igroup].sum();
 			if (sumAA > 0.)
 				mymodel.scale_correction[igroup] = sumXA / sumAA;
 			else
 				mymodel.scale_correction[igroup] = 1.;
-			avg_scale_correction += (double)(mymodel.nr_particles_group[igroup]) * mymodel.scale_correction[igroup];
-			nr_part += (double)(mymodel.nr_particles_group[igroup]);
+		}
+
+		// TODO! Avoid extremities in scale estimates, because they lead to catastrophic events and instabilities in refinement
+		// Let's exclude anything bigger than 5x the median or smaller than 1/5 of the median...
+		// Use the median instead of the mean, because it is much more robust to outliers.
+		std::vector<DOUBLE> sorted = mymodel.scale_correction;
+		std::sort(sorted.begin(), sorted.end());
+		DOUBLE median = sorted[mymodel.nr_groups / 2];
+
+		DOUBLE avg_scale_correction = 0., nr_part = 0.;
+		for (int igroup = 0; igroup < mymodel.nr_groups; igroup++)
+		{
+
+			if (mymodel.scale_correction[igroup] > 5. * median)
+				mymodel.scale_correction[igroup] = 5. * median;
+			else if (mymodel.scale_correction[igroup] < median / 5.)
+				mymodel.scale_correction[igroup] =  median / 5.;
 
+			avg_scale_correction += (DOUBLE)(mymodel.nr_particles_group[igroup]) * mymodel.scale_correction[igroup];
+			nr_part += (DOUBLE)(mymodel.nr_particles_group[igroup]);
 		}
 
 		// Constrain average scale_correction to one.
@@ -2015,18 +2288,26 @@ void MlOptimiser::maximizationOtherParameters()
 				mymodel.prior_offset_class[iclass].initZeros();
 		}
 
-		// Use sampling.NrDirections(0, true) to include all directions (also those with zero prior probability for any given image)
-		for (int idir = 0; idir < sampling.NrDirections(0, true); idir++)
+		// Use sampling.NrDirections() to include all directions (also those with zero prior probability for any given image)
+		if (!(do_skip_align || do_skip_rotate))
 		{
-			mymodel.pdf_direction[iclass](idir) = wsum_model.pdf_direction[iclass](idir) / sum_weight;
+			for (int idir = 0; idir < sampling.NrDirections(); idir++)
+			{
+				mymodel.pdf_direction[iclass](idir) = wsum_model.pdf_direction[iclass](idir) / sum_weight;
+			}
 		}
 	}
 
 	// Update sigma2_offset
 	// Factor 2 because of the 2-dimensionality of the xy-plane
 	if (!fix_sigma_offset)
-		mymodel.sigma2_offset = (wsum_model.sigma2_offset) / (2. * sum_weight);
+        {
+            if (mymodel.data_dim == 3) 
+                mymodel.sigma2_offset = (wsum_model.sigma2_offset) / (3. * sum_weight);
+            else
+                mymodel.sigma2_offset = (wsum_model.sigma2_offset) / (2. * sum_weight);
 
+        }
 	// TODO: update estimates for sigma2_rot, sigma2_tilt and sigma2_psi!
 
 	// Also refrain from updating sigma_noise after the first iteration with first_iter_cc!
@@ -2062,15 +2343,15 @@ void MlOptimiser::maximizationOtherParameters()
 		{
 			// Adjust the tau2_class and data_vs_prior_class, because they were calculated on the unfiltered maps
 			// This is merely a matter of having correct output in the model.star file (these values are not used in the calculations)
-			double radius = mymodel.ori_size * mymodel.pixel_size / ini_high;
+			DOUBLE radius = mymodel.ori_size * mymodel.pixel_size / ini_high;
 			radius -= WIDTH_FMASK_EDGE / 2.;
-			double radius_p = radius + WIDTH_FMASK_EDGE;
+			DOUBLE radius_p = radius + WIDTH_FMASK_EDGE;
 
 			for (int iclass = 0; iclass < mymodel.nr_classes; iclass++)
 			{
 				for (int rr = 0; rr < XSIZE(mymodel.tau2_class[iclass]); rr++)
 				{
-					double r = (double)rr;
+					DOUBLE r = (DOUBLE)rr;
 					if (r < radius)
 						continue;
 					else if (r > radius_p)
@@ -2080,7 +2361,7 @@ void MlOptimiser::maximizationOtherParameters()
 					}
 					else
 					{
-						double raisedcos = 0.5 - 0.5 * cos(PI * (radius_p - r) / WIDTH_FMASK_EDGE);
+						DOUBLE raisedcos = 0.5 - 0.5 * cos(PI * (radius_p - r) / WIDTH_FMASK_EDGE);
 						DIRECT_A1D_ELEM(mymodel.tau2_class[iclass], rr) *= raisedcos * raisedcos;
 						DIRECT_A1D_ELEM(mymodel.data_vs_prior_class[iclass], rr) *= raisedcos * raisedcos;
 					}
@@ -2117,17 +2398,17 @@ void MlOptimiser::solventFlatten()
 	std::cerr << "Entering MlOptimiser::solventFlatten" << std::endl;
 #endif
 	// First read solvent mask from disc, or pre-calculate it
-	Image<double> Isolvent, Isolvent2;
+	Image<DOUBLE> Isolvent, Isolvent2;
     Isolvent().resize(mymodel.Iref[0]);
 	Isolvent().setXmippOrigin();
 	Isolvent().initZeros();
 	if (fn_mask.contains("None"))
 	{
-		double radius = particle_diameter / (2. * mymodel.pixel_size);
-		double radius_p = radius + width_mask_edge;
+		DOUBLE radius = particle_diameter / (2. * mymodel.pixel_size);
+		DOUBLE radius_p = radius + width_mask_edge;
 		FOR_ALL_ELEMENTS_IN_ARRAY3D(Isolvent())
 		{
-			double r = sqrt((double)(k*k + i*i + j*j));
+			DOUBLE r = sqrt((DOUBLE)(k*k + i*i + j*j));
 			if (r < radius)
 				A3D_ELEM(Isolvent(), k, i, j) = 1.;
 			else if (r > radius_p)
@@ -2217,7 +2498,7 @@ void MlOptimiser::updateCurrentResolution()
 		// If we are not doing MAP-estimation, set maxres to Nyquist
 		maxres = mymodel.ori_size/2;
 	}
-    double newres = mymodel.getResolution(maxres);
+    DOUBLE newres = mymodel.getResolution(maxres);
 
 
     // Check whether resolution improved, if not increase nr_iter_wo_resol_gain
@@ -2275,9 +2556,9 @@ void MlOptimiser::updateImageSizeAndResolutionPointers()
     else if (adaptive_oversampling > 0.)
 	{
     	// Dependency of coarse_size on the angular sampling used in the first pass
-    	double rotated_distance = (sampling.getAngularSampling() / 360.) * PI * particle_diameter;
-		double keepsafe_factor = (mymodel.ref_dim == 3) ? 1.2 : 1.5;
-		double coarse_resolution = rotated_distance / keepsafe_factor;
+    	DOUBLE rotated_distance = (sampling.getAngularSampling() / 360.) * PI * particle_diameter;
+		DOUBLE keepsafe_factor = (mymodel.ref_dim == 3) ? 1.2 : 1.5;
+		DOUBLE coarse_resolution = rotated_distance / keepsafe_factor;
 		// Note coarse_size should be even-valued!
 		coarse_size = 2 * CEIL(mymodel.pixel_size * mymodel.ori_size / coarse_resolution);
 		// Coarse size can never be larger than max_coarse_size
@@ -2292,22 +2573,28 @@ void MlOptimiser::updateImageSizeAndResolutionPointers()
 
 	// Calculate number of pixels per resolution shell
 	Npix_per_shell.initZeros(mymodel.ori_size / 2 + 1);
-	MultidimArray<double> aux;
-	aux.resize(mymodel.ori_size, mymodel.ori_size / 2 + 1);
+	MultidimArray<DOUBLE> aux;
+	if (mymodel.data_dim == 3)
+		aux.resize(mymodel.ori_size, mymodel.ori_size, mymodel.ori_size / 2 + 1);
+	else
+		aux.resize(mymodel.ori_size, mymodel.ori_size / 2 + 1);
 	FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(aux)
 	{
-		int ires = ROUND(sqrt((double)(kp*kp + ip*ip + jp*jp)));
+		int ires = ROUND(sqrt((DOUBLE)(kp*kp + ip*ip + jp*jp)));
 		// TODO: better check for volume_refine, but the same still seems to hold... Half of the yz plane (either ip<0 or kp<0 is redundant at jp==0)
 		// Exclude points beyond XSIZE(Npix_per_shell), and exclude half of the x=0 column that is stored twice in FFTW
 		if (ires < mymodel.ori_size / 2 + 1 && !(jp==0 && ip < 0))
 			Npix_per_shell(ires) += 1;
 	}
 
-	Mresol_fine.resize(mymodel.current_size, mymodel.current_size / 2 + 1);
+	if (mymodel.data_dim == 3)
+		Mresol_fine.resize(mymodel.current_size, mymodel.current_size, mymodel.current_size / 2 + 1);
+	else
+		Mresol_fine.resize(mymodel.current_size, mymodel.current_size / 2 + 1);
 	Mresol_fine.initConstant(-1);
 	FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(Mresol_fine)
 	{
-		int ires = ROUND(sqrt((double)(kp*kp + ip*ip + jp*jp)));
+		int ires = ROUND(sqrt((DOUBLE)(kp*kp + ip*ip + jp*jp)));
 		// TODO: better check for volume_refine, but the same still seems to hold... Half of the yz plane (either ip<0 or kp<0 is redundant at jp==0)
 		// Exclude points beyond ires, and exclude and half (y<0) of the x=0 column that is stored twice in FFTW
 		if (ires < mymodel.current_size / 2 + 1  && !(jp==0 && ip < 0))
@@ -2316,11 +2603,15 @@ void MlOptimiser::updateImageSizeAndResolutionPointers()
 		}
 	}
 
-	Mresol_coarse.resize(coarse_size, coarse_size/ 2 + 1);
+	if (mymodel.data_dim == 3)
+		Mresol_coarse.resize(coarse_size, coarse_size, coarse_size/ 2 + 1);
+	else
+		Mresol_coarse.resize(coarse_size, coarse_size/ 2 + 1);
+
 	Mresol_coarse.initConstant(-1);
 	FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(Mresol_coarse)
 	{
-		int ires = ROUND(sqrt((double)(kp*kp + ip*ip + jp*jp)));
+		int ires = ROUND(sqrt((DOUBLE)(kp*kp + ip*ip + jp*jp)));
 		// Exclude points beyond ires, and exclude and half (y<0) of the x=0 column that is stored twice in FFTW
 		// exclude lowest-resolution points
 		if (ires < coarse_size / 2 + 1 && !(jp==0 && ip < 0))
@@ -2331,17 +2622,17 @@ void MlOptimiser::updateImageSizeAndResolutionPointers()
 
 //#define DEBUG_MRESOL
 #ifdef DEBUG_MRESOL
-	Image<double> img;
+	Image<DOUBLE> img;
 	img().resize(YSIZE(Mresol_fine),XSIZE(Mresol_fine));
 	FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(img())
 	{
-		DIRECT_MULTIDIM_ELEM(img(), n) = (double)DIRECT_MULTIDIM_ELEM(Mresol_fine, n);
+		DIRECT_MULTIDIM_ELEM(img(), n) = (DOUBLE)DIRECT_MULTIDIM_ELEM(Mresol_fine, n);
 	}
 	img.write("Mresol_fine.mrc");
 	img().resize(YSIZE(Mresol_coarse),XSIZE(Mresol_coarse));
 	FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(img())
 	{
-		DIRECT_MULTIDIM_ELEM(img(), n) = (double)DIRECT_MULTIDIM_ELEM(Mresol_coarse, n);
+		DIRECT_MULTIDIM_ELEM(img(), n) = (DOUBLE)DIRECT_MULTIDIM_ELEM(Mresol_coarse, n);
 	}
 	img.write("Mresol_coarse.mrc");
 #endif
@@ -2355,761 +2646,656 @@ void MlOptimiser::updateImageSizeAndResolutionPointers()
 }
 
 
-double MlOptimiser::calculatePdfOffset(Matrix1D<double> offset, Matrix1D<double> prior)
+void MlOptimiser::calculateRunningAveragesOfMovieFrames(long int my_ori_particle,
+		std::vector<MultidimArray<Complex > > &exp_Fimgs,
+		std::vector<MultidimArray<DOUBLE> > &exp_power_imgs,
+		std::vector<DOUBLE> &exp_highres_Xi2_imgs)
 {
-	if (mymodel.sigma2_offset < 0.0001)
-	{
-		return (offset.sum2() > 0.) ? 0. : 1.;
-	}
-	else
-	{
-		return exp ( (offset-prior).sum2() / (-2. * mymodel.sigma2_offset) ) / ( 2. * PI * mymodel.sigma2_offset );
-	}
-}
 
-void MlOptimiser::calculateRunningAveragesOfMovieFrames()
-{
 	std::vector<MultidimArray<Complex > > runavg_Fimgs;
 	std::vector<int> count_runavg;
 	MultidimArray<Complex > Fzero;
 	Fzero.resize(exp_Fimgs[0]);
 	Fzero.initZeros();
-
-	// initialise the sums at zero
-	for (int iimg = 0; iimg < exp_Fimgs.size(); iimg++)
-	{
-		runavg_Fimgs.push_back(Fzero);
-		count_runavg.push_back(0);
-	}
-
-	// running avgs NOT for series!
-	int iseries = 0;
-
-//#define DEBUG_RUNAVG
-#ifdef DEBUG_RUNAVG
-	FourierTransformer transformer;
-	MultidimArray< Complex > Fimg;
-	Image<double> It;
-	if (verb)
-	{
-		Fimg = exp_Fimgs[0];
-		It().resize(YSIZE(Fimg),YSIZE(Fimg));
-		transformer.inverseFourierTransform(Fimg, It());
-		CenterFFT(It(), false);
-		It.write("Fimg.spi");
-		std::cerr << "Written Fimg" << std::endl;
-	}
-#endif
+	runavg_Fimgs.resize(exp_Fimgs.size(), Fzero);
+	count_runavg.resize(exp_Fimgs.size(), 0);
 
 	// Calculate the running sums
-	for (int iimg = 0; iimg < exp_Fimgs.size(); iimg++)
+	for (int iframe = 0; iframe < exp_Fimgs.size(); iframe++)
 	{
-		// Who are we?
-		int my_ipart = exp_iimg_to_ipart[iimg];
-		long int my_ori_part_id = exp_ipart_to_ori_part_id[my_ipart];
-		long int my_part_id = exp_ipart_to_part_id[my_ipart];
-		int my_frame = exp_ipart_to_ori_part_nframe[my_ipart];
-
-#ifdef DEBUG_RUNAVG
-		if (verb)
-		{
-			long int my_img_id = mydata.getImageId(my_part_id, iseries);
-			FileName fntt;
-			mydata.MDimg.getValue(EMDL_IMAGE_NAME, fntt, my_img_id);
-			std::cerr << " my= " << fntt;
-		}
-#endif
 
-		long int my_first_runavg_frame = XMIPP_MAX(0, my_frame - movie_frame_running_avg_side);
-		long int my_last_runavg_frame = XMIPP_MIN(mydata.ori_particles[my_ori_part_id].particles_id.size() - 1, my_frame + movie_frame_running_avg_side);
+		long int my_first_runavg_frame = XMIPP_MAX(0, iframe - movie_frame_running_avg_side);
+		long int my_last_runavg_frame = XMIPP_MIN(exp_Fimgs.size() - 1, iframe + movie_frame_running_avg_side);
 
 		// Run over all images again and see which ones to sum
-		for (int iimg2 = 0; iimg2 < exp_Fimgs.size(); iimg2++)
+		for (int iframe2 = my_first_runavg_frame; iframe2 <=  my_last_runavg_frame; iframe2++)
 		{
-			int other_ipart = exp_iimg_to_ipart[iimg2];
-			long int other_ori_part_id = exp_ipart_to_ori_part_id[other_ipart];
-			long int other_part_id = exp_ipart_to_part_id[other_ipart];
-			int other_frame = exp_ipart_to_ori_part_nframe[other_ipart];
-
-			if (my_ori_part_id == other_ori_part_id && other_frame >= my_first_runavg_frame && other_frame <= my_last_runavg_frame)
-			{
-#ifdef DEBUG_RUNAVG
-				if (verb)
-				{
-					long int other_img_id = mydata.getImageId(other_part_id, iseries);
-					FileName fnt, fnm, fnp;
-					mydata.MDimg.getValue(EMDL_IMAGE_NAME, fnt, other_img_id);
-					mydata.MDimg.getValue(EMDL_PARTICLE_ORI_NAME, fnp, other_img_id);
-					mydata.MDimg.getValue(EMDL_MICROGRAPH_NAME, fnm, other_img_id);
-					std::cerr << " = " << fnt<<" "<<fnm<<" "<<fnp;
-				}
-#endif
-
-				// Add to sum
-				runavg_Fimgs[iimg] += exp_Fimgs[iimg2];
-				count_runavg[iimg] += 1;
-			}
+			// Add to sum
+			runavg_Fimgs[iframe] += exp_Fimgs[iframe2];
+			count_runavg[iframe] += 1;
 		}
-
-#ifdef DEBUG_RUNAVG
-		if (verb)
-			std::cerr << std::endl;
-#endif
 	}
 
 	// Calculate averages from sums and set back in exp_ vectors
-	for (int iimg = 0; iimg < exp_Fimgs.size(); iimg++)
+	for (int iframe = 0; iframe < exp_Fimgs.size(); iframe++)
 	{
-		double sum = (double)count_runavg[iimg];
-		FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(exp_Fimgs[iimg])
+		DOUBLE sum = (DOUBLE)count_runavg[iframe];
+		FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(exp_Fimgs[iframe])
 		{
-			DIRECT_MULTIDIM_ELEM(exp_Fimgs[iimg], n) = DIRECT_MULTIDIM_ELEM(runavg_Fimgs[iimg], n) / sum;
+			DIRECT_MULTIDIM_ELEM(exp_Fimgs[iframe], n) = DIRECT_MULTIDIM_ELEM(runavg_Fimgs[iframe], n) / sum;
 		}
 		// Also lower the power of the images for the sigma2_noise and diff2 calculations beyond current_size....
 		// sigma2_(a+b) = sigma2_(a) + sigma2_(b)
 		// The actual values are lost, just hope the images obey statistics...
-		exp_power_imgs[iimg] /= sum;
-		exp_highres_Xi2_imgs[iimg] /= sum;
-	}
-#ifdef DEBUG_RUNAVG
-	if (verb)
-	{
-		Fimg = exp_Fimgs[0];
-		It().resize(YSIZE(Fimg),YSIZE(Fimg));
-		transformer.inverseFourierTransform(Fimg, It());
-		CenterFFT(It(), false);
-		It.write("Frunavg.spi");
-		std::cerr << "Written Frunavg.spi, sleeping 2 seconds..." << std::endl;
-		sleep(2);
-
+		exp_power_imgs[iframe] /= sum;
+		exp_highres_Xi2_imgs[iframe] /= sum;
 	}
-#endif
 
 }
 
-void MlOptimiser::doThreadGetFourierTransformsAndCtfs(int thread_id)
+void MlOptimiser::getFourierTransformsAndCtfs(long int my_ori_particle, int metadata_offset,
+		std::vector<MultidimArray<Complex > > &exp_Fimgs,
+		std::vector<MultidimArray<Complex > > &exp_Fimgs_nomask,
+		std::vector<MultidimArray<DOUBLE> > &exp_Fctfs,
+		std::vector<Matrix1D<DOUBLE> > &exp_old_offset,
+		std::vector<Matrix1D<DOUBLE> > &exp_prior,
+		std::vector<MultidimArray<DOUBLE> > &exp_power_imgs,
+		std::vector<DOUBLE> &exp_highres_Xi2_imgs,
+		std::vector<int> &exp_pointer_dir_nonzeroprior,
+		std::vector<int> &exp_pointer_psi_nonzeroprior,
+		std::vector<DOUBLE> &exp_directions_prior,
+		std::vector<DOUBLE> &exp_psi_prior)
 {
-	// Only first thread initialises
-	if (thread_id == 0)
-	{
-		exp_starting_image_no.clear();
-		exp_power_imgs.clear();
-		exp_highres_Xi2_imgs.clear();
-		exp_Fimgs.clear();
-		exp_Fimgs_nomask.clear();
-		exp_Fctfs.clear();
-		exp_old_offset.clear();
-		exp_prior.clear();
-		exp_local_oldcc.clear();
-		exp_ipart_to_part_id.clear();
-		exp_ipart_to_ori_part_id.clear();
-		exp_ipart_to_ori_part_nframe.clear();
-		exp_iimg_to_ipart.clear();
-
-		// Resize to the right size instead of using pushbacks
-		exp_starting_image_no.resize(exp_nr_particles);
-
-		// First check how many images there are in the series for each particle...
-		// And calculate exp_nr_images
-		exp_nr_images = 0;
-		for (long int ori_part_id = exp_my_first_ori_particle, my_image_no = 0, ipart = 0; ori_part_id <= exp_my_last_ori_particle; ori_part_id++)
-	    {
-
-#ifdef DEBUG_CHECKSIZES
-			if (ori_part_id >= mydata.ori_particles.size())
-			{
-				std::cerr<< "ori_part_id= "<<ori_part_id<<" mydata.ori_particles.size()= "<< mydata.ori_particles.size() <<std::endl;
-				REPORT_ERROR("ori_part_id >= mydata.ori_particles.size()");
-			}
-#endif
-			for (long int i = 0; i < mydata.ori_particles[ori_part_id].particles_id.size(); i++, ipart++)
-			{
-				long int part_id = mydata.ori_particles[ori_part_id].particles_id[i];
-				int iipart = exp_ipart_to_part_id.size();
-				exp_starting_image_no.at(iipart) = exp_nr_images;
-				exp_nr_images += mydata.getNrImagesInSeries(part_id);
-				exp_ipart_to_part_id.push_back(part_id);
-				exp_ipart_to_ori_part_id.push_back(ori_part_id);
-				exp_ipart_to_ori_part_nframe.push_back(i);
-				for (int i = 0; i < mydata.getNrImagesInSeries(part_id); i++)
-					exp_iimg_to_ipart.push_back(iipart);
-			}
-	    }
-		// Then also resize vectors for all images
-		exp_power_imgs.resize(exp_nr_images);
-		exp_highres_Xi2_imgs.resize(exp_nr_images);
-		exp_Fimgs.resize(exp_nr_images);
-		exp_Fimgs_nomask.resize(exp_nr_images);
-		exp_Fctfs.resize(exp_nr_images);
-		exp_old_offset.resize(exp_nr_images);
-		exp_prior.resize(exp_nr_images);
-		exp_local_oldcc.resize(exp_nr_images);
-
-	}
-	global_barrier->wait();
 
 	FourierTransformer transformer;
-	size_t first_ipart = 0, last_ipart = 0;
-	while (exp_ipart_ThreadTaskDistributor->getTasks(first_ipart, last_ipart))
-	{
 
-		for (long int ipart = first_ipart; ipart <= last_ipart; ipart++)
+	for (int ipart = 0; ipart < mydata.ori_particles[my_ori_particle].particles_id.size(); ipart++)
+	{
+		FileName fn_img;
+		Image<DOUBLE> img, rec_img;
+		MultidimArray<Complex > Fimg, Faux;
+		MultidimArray<DOUBLE> Fctf;
+
+		// Get the right line in the exp_fn_img strings (also exp_fn_recimg and exp_fn_ctfs)
+		int istop = 0;
+		for (long int ii = exp_my_first_ori_particle; ii < my_ori_particle; ii++)
+			istop += mydata.ori_particles[ii].particles_id.size();
+		istop += ipart;
+
+		// What is my particle_id?
+		long int part_id = mydata.ori_particles[my_ori_particle].particles_id[ipart];
+		// Which group do I belong?
+		int group_id = mydata.getGroupId(part_id);
+
+		// Get the norm_correction
+		DOUBLE normcorr = DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_NORM);
+
+		// Get the optimal origin offsets from the previous iteration
+		Matrix1D<DOUBLE> my_old_offset(2), my_prior(2);
+		XX(my_old_offset) = DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_XOFF);
+		YY(my_old_offset) = DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_YOFF);
+		XX(my_prior)      = DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_XOFF_PRIOR);
+		YY(my_prior)      = DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_YOFF_PRIOR);
+		// Uninitialised priors were set to 999.
+		if (XX(my_prior) > 998.99 && XX(my_prior) < 999.01)
+			XX(my_prior) = 0.;
+		if (YY(my_prior) > 998.99 && YY(my_prior) < 999.01)
+			YY(my_prior) = 0.;
+
+		if (mymodel.data_dim == 3)
 		{
-			// the exp_ipart_ThreadTaskDistributor was set with nr_pool,
-			// but some, e.g. the last, batch of pooled particles may be smaller
-			if (ipart >= exp_nr_particles)
-				break;
+			my_old_offset.resize(3);
+			my_prior.resize(3);
+			ZZ(my_old_offset) = DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_ZOFF);
+			ZZ(my_prior)      = DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_ZOFF_PRIOR);
+			// Unitialised priors were set to 999.
+			if (ZZ(my_prior) > 998.99 && ZZ(my_prior) < 999.01)
+				ZZ(my_prior) = 0.;
+		}
 
-#ifdef DEBUG_CHECKSIZES
-			if (ipart >= exp_ipart_to_part_id.size())
+		if (mymodel.orientational_prior_mode != NOPRIOR && !(do_skip_align || do_skip_rotate))
+		{
+			// First try if there are some fixed prior angles
+			DOUBLE prior_rot = DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_ROT_PRIOR);
+			DOUBLE prior_tilt = DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_TILT_PRIOR);
+			DOUBLE prior_psi = DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_PSI_PRIOR);
+
+			// If there were no defined priors (i.e. their values were 999.), then use the "normal" angles
+			if (prior_rot > 998.99 && prior_rot < 999.01)
+				prior_rot = DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_ROT);
+			if (prior_tilt > 998.99 && prior_tilt < 999.01)
+				prior_tilt = DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_TILT);
+			if (prior_psi > 998.99 && prior_psi < 999.01)
+				prior_psi = DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_PSI);
+
+			////////// TODO TODO TODO
+			////////// How does this work now: each particle has a different sampling object?!!!
+			// Select only those orientations that have non-zero prior probability
+			sampling.selectOrientationsWithNonZeroPriorProbability(prior_rot, prior_tilt, prior_psi,
+					sqrt(mymodel.sigma2_rot), sqrt(mymodel.sigma2_tilt), sqrt(mymodel.sigma2_psi),
+					exp_pointer_dir_nonzeroprior, exp_directions_prior, exp_pointer_psi_nonzeroprior, exp_psi_prior);
+
+			long int nr_orients = sampling.NrDirections(0, &exp_pointer_dir_nonzeroprior) * sampling.NrPsiSamplings(0, &exp_pointer_psi_nonzeroprior);
+			if (nr_orients == 0)
 			{
-				std::cerr<< "ipart= "<<ipart<<" exp_ipart_to_part_id.size()= "<< exp_ipart_to_part_id.size() <<std::endl;
-				REPORT_ERROR("ipart >= exp_ipart_to_part_id.size()");
+				std::cerr << " sampling.NrDirections()= " << sampling.NrDirections(0, &exp_pointer_dir_nonzeroprior)
+						<< " sampling.NrPsiSamplings()= " << sampling.NrPsiSamplings(0, &exp_pointer_psi_nonzeroprior) << std::endl;
+				REPORT_ERROR("Zero orientations fall within the local angular search. Increase the sigma-value(s) on the orientations!");
 			}
-#endif
-			long int part_id = exp_ipart_to_part_id[ipart];
 
-			// Prevent movies and series at the same time...
-			if (mydata.getNrImagesInSeries(part_id) > 1 && do_realign_movies)
-				REPORT_ERROR("Not ready yet for dealing with image series at the same time as realigning movie frames....");
+		}
 
-			for (int iseries = 0; iseries < mydata.getNrImagesInSeries(part_id); iseries++)
+		// Get the image and recimg data
+		if (do_parallel_disc_io)
+		{
+			// If all slaves had preread images into RAM: get those now
+			if (do_preread_images)
+			{
+				img() = mydata.particles[part_id].img;
+			}
+			else
 			{
+                            // Read from disc
+                            FileName fn_img;
+                            std::istringstream split(exp_fn_img);
+                            for (int i = 0; i <= istop; i++)
+				getline(split, fn_img);
+
+                            img.read(fn_img);
+                            img().setXmippOrigin();
+                            if (has_converged && do_use_reconstruct_images)
+                            {
+				FileName fn_recimg;
+				std::istringstream split2(exp_fn_recimg);
+				// Get the right line in the exp_fn_img string
+				for (int i = 0; i <= istop; i++)
+                                    getline(split2, fn_recimg);
+				rec_img.read(fn_recimg);
+				rec_img().setXmippOrigin();
+                            }
+                        }
+		}
+		else
+		{
 
-				FileName fn_img;
-				Image<double> img, rec_img;
-				MultidimArray<Complex > Fimg, Faux;
-				MultidimArray<double> Fctf;
-				int my_image_no = exp_starting_image_no.at(ipart) + iseries;
-				// Which group do I belong?
-				int group_id = mydata.getGroupId(part_id, iseries);
-
-				// Get the norm_correction
-				double normcorr = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NORM);
-
-				// Get the optimal origin offsets from the previous iteration
-				Matrix1D<double> my_old_offset(2), my_prior(2);
-				XX(my_old_offset) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_XOFF);
-				YY(my_old_offset) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_YOFF);
-				XX(my_prior)      = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_XOFF_PRIOR);
-				YY(my_prior)      = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_YOFF_PRIOR);
-				// Uninitialised priors were set to 999.
-				if (XX(my_prior) > 998.99 && XX(my_prior) < 999.01)
-					XX(my_prior) = 0.;
-				if (YY(my_prior) > 998.99 && YY(my_prior) < 999.01)
-					YY(my_prior) = 0.;
-
-				// Get the old cross-correlations
-				exp_local_oldcc.at(my_image_no) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_DLL);
-
-				// If we do local angular searches, get the previously assigned angles to center the prior
-				// Only do this for the first image in the series, as this prior work per-particle, not per-image
-				// All images in the series use the same rotational sampling, brought back to "exp_R_mic=identity"
-				if (do_skip_align || do_skip_rotate)
+			// Unpack the image from the imagedata
+			if (mymodel.data_dim == 3)
+			{
+				img().resize(mymodel.ori_size, mymodel.ori_size, mymodel.ori_size);
+				// Only allow a single image per call of this function!!! nr_pool needs to be set to 1!!!!
+				// This will save memory, as we'll need to store all translated images in memory....
+				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(img())
 				{
-					// No need to block the threads global_mutex, as nr_pool will be set to 1 anyway for do_skip_align!
-					if (do_skip_align)
-					{
-						// Rounded translations will be applied to the image upon reading,
-						// set the unique translation in the sampling object to the fractional difference
-						Matrix1D<double> rounded_offset = my_old_offset;
-						rounded_offset.selfROUND();
-						rounded_offset = my_old_offset - rounded_offset;
-						sampling.setOneTranslation(rounded_offset);
-					}
-
-					// Also set the rotations
-					double old_rot, old_tilt, old_psi;
-					old_rot = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ROT);
-					old_tilt = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_TILT);
-					old_psi = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PSI);
-					sampling.setOneOrientation(old_rot, old_tilt, old_psi);
-
+					DIRECT_A3D_ELEM(img(), k, i, j) = DIRECT_A3D_ELEM(exp_imagedata, k, i, j);
 				}
-				else if (mymodel.orientational_prior_mode != NOPRIOR && iseries == 0)
-				{
-					// First try if there are some fixed prior angles
-					double prior_rot = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ROT_PRIOR);
-					double prior_tilt = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_TILT_PRIOR);
-					double prior_psi = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PSI_PRIOR);
-
-					// If there were no defined priors (i.e. their values were 999.), then use the "normal" angles
-					if (prior_rot > 998.99 && prior_rot < 999.01)
-						prior_rot = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ROT);
-					if (prior_tilt > 998.99 && prior_tilt < 999.01)
-						prior_tilt = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_TILT);
-					if (prior_psi > 998.99 && prior_psi < 999.01)
-						prior_psi = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PSI);
-
-					// For tilted series: convert the angles back onto the untilted ones...
-					// Calculate the angles back from the Euler matrix because for tilt series exp_R_mic may have changed them...
-					Matrix2D<double> A, R_mic(3,3);
-					R_mic(0,0) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_0_0);
-					R_mic(0,1) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_0_1);
-					R_mic(0,2) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_0_2);
-					R_mic(1,0) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_1_0);
-					R_mic(1,1) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_1_1);
-					R_mic(1,2) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_1_2);
-					R_mic(2,0) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_2_0);
-					R_mic(2,1) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_2_1);
-					R_mic(2,2) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_2_2);
-					if (!R_mic.isIdentity())
-					{
-						Euler_angles2matrix(prior_rot, prior_tilt, prior_psi, A);
-						A = R_mic.inv() * A;
-						Euler_matrix2angles(A, prior_rot, prior_tilt, prior_psi);
-					}
-
-					global_mutex.lock();
-
-					// Select only those orientations that have non-zero prior probability
-					sampling.selectOrientationsWithNonZeroPriorProbability(prior_rot, prior_tilt, prior_psi,
-							sqrt(mymodel.sigma2_rot), sqrt(mymodel.sigma2_tilt), sqrt(mymodel.sigma2_psi));
+				img().setXmippOrigin();
 
-					long int nr_orients = sampling.NrDirections() * sampling.NrPsiSamplings();
-					if (nr_orients == 0)
+				if (has_converged && do_use_reconstruct_images)
+				{
+					rec_img().resize(mymodel.ori_size, mymodel.ori_size, mymodel.ori_size);
+					int offset = (do_ctf_correction) ? 2 * mymodel.ori_size : mymodel.ori_size;
+					FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(rec_img())
 					{
-						std::cerr << " sampling.NrDirections()= " << sampling.NrDirections() << " sampling.NrPsiSamplings()= " << sampling.NrPsiSamplings() << std::endl;
-						REPORT_ERROR("Zero orientations fall within the local angular search. Increase the sigma-value(s) on the orientations!");
+						DIRECT_A3D_ELEM(rec_img(), k, i, j) = DIRECT_A3D_ELEM(exp_imagedata, offset + k, i, j);
 					}
-					int threadBlockSize = (nr_orients > 100) ? 10 : 1;
-
-					exp_iorient_ThreadTaskDistributor->resize(nr_orients, threadBlockSize);
-
-					global_mutex.unlock();
+					rec_img().setXmippOrigin();
 
 				}
 
-				// Unpack the image from the imagedata
+			}
+			else
+			{
 				img().resize(mymodel.ori_size, mymodel.ori_size);
 				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY2D(img())
 				{
-					DIRECT_A2D_ELEM(img(), i, j) = DIRECT_A3D_ELEM(exp_imagedata, my_image_no, i, j);
+					DIRECT_A2D_ELEM(img(), i, j) = DIRECT_A3D_ELEM(exp_imagedata, metadata_offset + ipart, i, j);
 				}
 				img().setXmippOrigin();
 				if (has_converged && do_use_reconstruct_images)
 				{
+
+					////////////// TODO: think this through for no-threads here.....
 					rec_img().resize(mymodel.ori_size, mymodel.ori_size);
 					FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY2D(rec_img())
 					{
-						DIRECT_A2D_ELEM(rec_img(), i, j) = DIRECT_A3D_ELEM(exp_imagedata, exp_nr_images + my_image_no, i, j);
+						DIRECT_A2D_ELEM(rec_img(), i, j) = DIRECT_A3D_ELEM(exp_imagedata, exp_nr_images + metadata_offset + ipart, i, j);
 					}
 					rec_img().setXmippOrigin();
 				}
+			}
+		}
 //#define DEBUG_SOFTMASK
 #ifdef DEBUG_SOFTMASK
-					Image<double> tt;
-					tt()=img();
-					tt.write("Fimg_unmasked.spi");
-					std::cerr << "written Fimg_unmasked.spi; press any key to continue..." << std::endl;
-					char c;
-					std::cin >> c;
-#endif
-				// Apply the norm_correction term
-				if (do_norm_correction)
-				{
+		Image<DOUBLE> tt;
+		tt()=img();
+		tt.write("Fimg_unmasked.spi");
+		std::cerr << "written Fimg_unmasked.spi; press any key to continue..." << std::endl;
+		char c;
+		std::cin >> c;
+#endif
+		// Apply the norm_correction term
+		if (do_norm_correction)
+		{
 //#define DEBUG_NORM
 #ifdef DEBUG_NORM
-					if (normcorr < 0.001 || normcorr > 1000. || mymodel.avg_norm_correction < 0.001 || mymodel.avg_norm_correction > 1000.)
-					{
-						std::cerr << " ** normcorr= " << normcorr << std::endl;
-						std::cerr << " ** mymodel.avg_norm_correction= " << mymodel.avg_norm_correction << std::endl;
-						std::cerr << " ** fn_img= " << fn_img << " part_id= " << part_id << std::endl;
-						std::cerr << " ** iseries= " << iseries << " ipart= " << ipart << " part_id= " << part_id << std::endl;
-						int group_id = mydata.getGroupId(part_id, iseries);
-						std::cerr << " ml_model.sigma2_noise[group_id]= " << mymodel.sigma2_noise[group_id] << " group_id= " << group_id <<std::endl;
-						std::cerr << " part_id= " << part_id << " iseries= " << iseries << std::endl;
-						std::cerr << " img_id= " << img_id << std::endl;
-						REPORT_ERROR("Very small or very big (avg) normcorr!");
-					}
+			if (normcorr < 0.001 || normcorr > 1000. || mymodel.avg_norm_correction < 0.001 || mymodel.avg_norm_correction > 1000.)
+			{
+				std::cerr << " ** normcorr= " << normcorr << std::endl;
+				std::cerr << " ** mymodel.avg_norm_correction= " << mymodel.avg_norm_correction << std::endl;
+				std::cerr << " ** fn_img= " << fn_img << " part_id= " << part_id << std::endl;
+				std::cerr << " ** iseries= " << iseries << " ipart= " << ipart << " part_id= " << part_id << std::endl;
+				int group_id = mydata.getGroupId(part_id);
+				std::cerr << " ml_model.sigma2_noise[group_id]= " << mymodel.sigma2_noise[group_id] << " group_id= " << group_id <<std::endl;
+				std::cerr << " part_id= " << part_id << " iseries= " << iseries << std::endl;
+				std::cerr << " img_id= " << img_id << std::endl;
+				REPORT_ERROR("Very small or very big (avg) normcorr!");
+			}
 #endif
-					img() *= mymodel.avg_norm_correction / normcorr;
-				}
-
-				// Apply (rounded) old offsets first
-				my_old_offset.selfROUND();
-				selfTranslate(img(), my_old_offset, DONT_WRAP);
-				if (has_converged && do_use_reconstruct_images)
-					selfTranslate(rec_img(), my_old_offset, DONT_WRAP);
-
-				exp_old_offset.at(my_image_no) = my_old_offset;
-				// Also store priors on translations
-				exp_prior.at(my_image_no) = my_prior;
-
-				// Always store FT of image without mask (to be used for the reconstruction)
-				MultidimArray<double> img_aux;
-				img_aux = (has_converged && do_use_reconstruct_images) ? rec_img() : img();
-				CenterFFT(img_aux, true);
-				transformer.FourierTransform(img_aux, Faux);
-				windowFourierTransform(Faux, Fimg, mymodel.current_size);
-
-				// Here apply the beamtilt correction if necessary
-				// This will only be used for reconstruction, not for alignment
-				// But beamtilt only affects very high-resolution components anyway...
-				//
-				double beamtilt_x = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_BEAMTILT_X);
-				double beamtilt_y = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_BEAMTILT_Y);
-				double Cs = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_CS);
-				double V = 1000. * DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_VOLTAGE);
-				double lambda = 12.2643247 / sqrt(V * (1. + V * 0.978466e-6));
-				if (ABS(beamtilt_x) > 0. || ABS(beamtilt_y) > 0.)
-					selfApplyBeamTilt(Fimg, beamtilt_x, beamtilt_y, lambda, Cs, mymodel.pixel_size, mymodel.ori_size);
-
-				exp_Fimgs_nomask.at(my_image_no) = Fimg;
-
-				long int ori_part_id = exp_ipart_to_ori_part_id[ipart];
-
-				MultidimArray<double> Mnoise;
-				if (!do_zero_mask)
-				{
-					// Make a noisy background image with the same spectrum as the sigma2_noise
-
-					// Different MPI-distributed subsets may otherwise have different instances of the random noise below,
-					// because work is on an on-demand basis and therefore variable with the timing of distinct nodes...
-					// Have the seed based on the ipart, so that each particle has a different instant of the noise
-					// Do this all inside a mutex for the threads, because they all use the same static variables inside ran1...
-					// (So the mutex only goal is to make things exactly reproducible with the same random_seed.)
-					global_mutex.lock();
-
-					//init_random_generator(random_seed + ori_part_id);
-					if (do_realign_movies)
-						init_random_generator(random_seed + part_id);
-					else
-						init_random_generator(random_seed + ori_part_id);
-
-					// If we're doing running averages, then the sigma2_noise was already adjusted for the running averages.
-					// Undo this adjustment here in order to get the right noise in the individual frames
-					MultidimArray<double> power_noise = sigma2_fudge * mymodel.sigma2_noise[group_id];
-					if (do_realign_movies)
-						power_noise *= (2. * movie_frame_running_avg_side + 1.);
-
-					// Create noisy image for outside the mask
-					MultidimArray<Complex > Fnoise;
-					Mnoise.resize(img());
-					transformer.setReal(Mnoise);
-					transformer.getFourierAlias(Fnoise);
-					// Fill Fnoise with random numbers, use power spectrum of the noise for its variance
-					FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(Fnoise)
-					{
-						int ires = ROUND( sqrt( (double)(kp * kp + ip * ip + jp * jp) ) );
-						if (ires >= 0 && ires < XSIZE(Fnoise))
-						{
-							double sigma = sqrt(DIRECT_A1D_ELEM(power_noise, ires));
-							DIRECT_A3D_ELEM(Fnoise, k, i, j).real = rnd_gaus(0., sigma);
-							DIRECT_A3D_ELEM(Fnoise, k, i, j).imag = rnd_gaus(0., sigma);
-						}
-						else
-						{
-							DIRECT_A3D_ELEM(Fnoise, k, i, j) = 0.;
-						}
-					}
-					// Back to real space Mnoise
-					transformer.inverseFourierTransform();
-					Mnoise.setXmippOrigin();
-
-					// unlock the mutex now that all calss to random functions have finished
-					global_mutex.unlock();
+			img() *= mymodel.avg_norm_correction / normcorr;
+		}
 
-					softMaskOutsideMap(img(), particle_diameter / (2. * mymodel.pixel_size), (double)width_mask_edge, &Mnoise);
+		// Apply (rounded) old offsets first
+		my_old_offset.selfROUND();
+		selfTranslate(img(), my_old_offset, DONT_WRAP);
+		if (has_converged && do_use_reconstruct_images)
+			selfTranslate(rec_img(), my_old_offset, DONT_WRAP);
+
+		exp_old_offset[ipart] = my_old_offset;
+		// Also store priors on translations
+		exp_prior[ipart] = my_prior;
+
+		// Always store FT of image without mask (to be used for the reconstruction)
+		MultidimArray<DOUBLE> img_aux;
+		img_aux = (has_converged && do_use_reconstruct_images) ? rec_img() : img();
+		CenterFFT(img_aux, true);
+		transformer.FourierTransform(img_aux, Faux);
+		windowFourierTransform(Faux, Fimg, mymodel.current_size);
+
+		// Here apply the beamtilt correction if necessary
+		// This will only be used for reconstruction, not for alignment
+		// But beamtilt only affects very high-resolution components anyway...
+		//
+		DOUBLE beamtilt_x = DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_BEAMTILT_X);
+		DOUBLE beamtilt_y = DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_BEAMTILT_Y);
+		DOUBLE Cs = DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_CTF_CS);
+		DOUBLE V = 1000. * DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_CTF_VOLTAGE);
+		DOUBLE lambda = 12.2643247 / sqrt(V * (1. + V * 0.978466e-6));
+		if (ABS(beamtilt_x) > 0. || ABS(beamtilt_y) > 0.)
+			selfApplyBeamTilt(Fimg, beamtilt_x, beamtilt_y, lambda, Cs, mymodel.pixel_size, mymodel.ori_size);
+
+		exp_Fimgs_nomask.at(ipart) = Fimg;
+
+
+		MultidimArray<DOUBLE> Mnoise;
+		if (!do_zero_mask)
+		{
+			// Make a noisy background image with the same spectrum as the sigma2_noise
 
+			// Different MPI-distributed subsets may otherwise have different instances of the random noise below,
+			// because work is on an on-demand basis and therefore variable with the timing of distinct nodes...
+			// Have the seed based on the part_id, so that each particle has a different instant of the noise
+			if (do_realign_movies)
+				init_random_generator(random_seed + part_id);
+			else
+				init_random_generator(random_seed + my_ori_particle); // This only serves for exact reproducibility tests with 1.3-code...
+
+			// If we're doing running averages, then the sigma2_noise was already adjusted for the running averages.
+			// Undo this adjustment here in order to get the right noise in the individual frames
+			MultidimArray<DOUBLE> power_noise = sigma2_fudge * mymodel.sigma2_noise[group_id];
+			if (do_realign_movies)
+				power_noise *= (2. * movie_frame_running_avg_side + 1.);
+
+			// Create noisy image for outside the mask
+			MultidimArray<Complex > Fnoise;
+			Mnoise.resize(img());
+			transformer.setReal(Mnoise);
+			transformer.getFourierAlias(Fnoise);
+			// Fill Fnoise with random numbers, use power spectrum of the noise for its variance
+			FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(Fnoise)
+			{
+				int ires = ROUND( sqrt( (DOUBLE)(kp * kp + ip * ip + jp * jp) ) );
+				if (ires >= 0 && ires < XSIZE(Fnoise))
+				{
+					DOUBLE sigma = sqrt(DIRECT_A1D_ELEM(power_noise, ires));
+					DIRECT_A3D_ELEM(Fnoise, k, i, j).real = rnd_gaus(0., sigma);
+					DIRECT_A3D_ELEM(Fnoise, k, i, j).imag = rnd_gaus(0., sigma);
 				}
 				else
 				{
-					softMaskOutsideMap(img(), particle_diameter / (2. * mymodel.pixel_size), (double)width_mask_edge);
+					DIRECT_A3D_ELEM(Fnoise, k, i, j) = 0.;
 				}
+			}
+			// Back to real space Mnoise
+			transformer.inverseFourierTransform();
+			Mnoise.setXmippOrigin();
+
+			softMaskOutsideMap(img(), particle_diameter / (2. * mymodel.pixel_size), (DOUBLE)width_mask_edge, &Mnoise);
+
+		}
+		else
+		{
+			softMaskOutsideMap(img(), particle_diameter / (2. * mymodel.pixel_size), (DOUBLE)width_mask_edge);
+		}
 #ifdef DEBUG_SOFTMASK
-					tt()=img();
-					tt.write("Fimg_masked.spi");
-					std::cerr << "written Fimg_masked.spi; press any key to continue..." << std::endl;
-					exit(1);
-					std::cin >> c;
+		tt()=img();
+		tt.write("Fimg_masked.spi");
+		std::cerr << "written Fimg_masked.spi; press any key to continue..." << std::endl;
+		std::cin >> c;
 #endif
 
-				// Inside Projector and Backprojector the origin of the Fourier Transform is centered!
-				CenterFFT(img(), true);
+		// Inside Projector and Backprojector the origin of the Fourier Transform is centered!
+		CenterFFT(img(), true);
+
+		// Store the Fourier Transform of the image Fimg
+		transformer.FourierTransform(img(), Faux);
 
-				// Store the Fourier Transform of the image Fimg
-				transformer.FourierTransform(img(), Faux);
+		// Store the power_class spectrum of the whole image (to fill sigma2_noise between current_size and ori_size
+		if (mymodel.current_size < mymodel.ori_size)
+		{
+			MultidimArray<DOUBLE> spectrum;
+			spectrum.initZeros(mymodel.ori_size/2 + 1);
+			DOUBLE highres_Xi2 = 0.;
+			FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(Faux)
+			{
+				int ires = ROUND( sqrt( (DOUBLE)(kp*kp + ip*ip + jp*jp) ) );
+				// Skip Hermitian pairs in the x==0 column
 
-				// Store the power_class spectrum of the whole image (to fill sigma2_noise between current_size and ori_size
-				if (mymodel.current_size < mymodel.ori_size)
+				if (ires > 0 && ires < mymodel.ori_size/2 + 1 && !(jp==0 && ip < 0) )
 				{
-					MultidimArray<double> spectrum;
-					spectrum.initZeros(mymodel.ori_size/2 + 1);
-					double highres_Xi2 = 0.;
-					FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(Faux)
-					{
-						int ires = ROUND( sqrt( (double)(kp*kp + ip*ip + jp*jp) ) );
-						// Skip Hermitian pairs in the x==0 column
+					DOUBLE normFaux = norm(DIRECT_A3D_ELEM(Faux, k, i, j));
+					DIRECT_A1D_ELEM(spectrum, ires) += normFaux;
+					// Store sumXi2 from current_size until ori_size
+					if (ires >= mymodel.current_size/2 + 1)
+						highres_Xi2 += normFaux;
+				}
+			}
 
-						if (ires > 0 && ires < mymodel.ori_size/2 + 1 && !(jp==0 && ip < 0) )
-						{
-							double normFaux = norm(DIRECT_A3D_ELEM(Faux, k, i, j));
-							DIRECT_A1D_ELEM(spectrum, ires) += normFaux;
-							// Store sumXi2 from current_size until ori_size
-							if (ires >= mymodel.current_size/2 + 1)
-								highres_Xi2 += normFaux;
-						}
-					}
+			// Let's use .at() here instead of [] to check whether we go outside the vectors bounds
+			exp_power_imgs.at(ipart) = spectrum;
+			exp_highres_Xi2_imgs.at(ipart) = highres_Xi2;
+		}
+		else
+		{
+			exp_highres_Xi2_imgs.at(ipart) = 0.;
+		}
+
+		// We never need any resolutions higher than current_size
+		// So resize the Fourier transforms
+		windowFourierTransform(Faux, Fimg, mymodel.current_size);
 
-					// Let's use .at() here instead of [] to check whether we go outside the vectors bounds
-					exp_power_imgs.at(my_image_no) = spectrum;
-					exp_highres_Xi2_imgs.at(my_image_no) = highres_Xi2;
+		// Also store its CTF
+		Fctf.resize(Fimg);
+
+		// Now calculate the actual CTF
+		if (do_ctf_correction)
+		{
+			if (mymodel.data_dim == 3)
+			{
+				Image<DOUBLE> Ictf;
+				if (do_parallel_disc_io)
+				{
+					// Read CTF-image from disc
+					FileName fn_ctf;
+					std::istringstream split(exp_fn_ctf);
+					// Get the right line in the exp_fn_img string
+					for (int i = 0; i <= istop; i++)
+						getline(split, fn_ctf);
+					Ictf.read(fn_ctf);
 				}
 				else
 				{
-					exp_highres_Xi2_imgs.at(my_image_no) = 0.;
+					// Unpack the CTF-image from the exp_imagedata array
+					Ictf().resize(mymodel.ori_size, mymodel.ori_size, mymodel.ori_size);
+					FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Ictf())
+					{
+						DIRECT_A3D_ELEM(Ictf(), k, i, j) = DIRECT_A3D_ELEM(exp_imagedata, mymodel.ori_size + k, i, j);
+					}
 				}
-
-				// We never need any resolutions higher than current_size
-				// So resize the Fourier transforms
-				windowFourierTransform(Faux, Fimg, mymodel.current_size);
-
-				// Also store its CTF
-				Fctf.resize(Fimg);
-
-				// Now calculate the actual CTF
-				if (do_ctf_correction)
+				// Set the CTF-image in Fctf
+				Ictf().setXmippOrigin();
+				FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(Fctf)
 				{
-					CTF ctf;
-					ctf.setValues(DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_DEFOCUS_U),
-							DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_DEFOCUS_V),
-							DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_DEFOCUS_ANGLE),
-							DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_VOLTAGE),
-							DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_CS),
-							DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_Q0),
-							DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_BFAC));
-
-					ctf.getFftwImage(Fctf, mymodel.ori_size, mymodel.ori_size, mymodel.pixel_size,
-							ctf_phase_flipped, only_flip_phases, intact_ctf_first_peak, true);
+					// Use negative kp,ip and jp indices, because the origin in the ctf_img lies half a pixel to the right of the actual center....
+					DIRECT_A3D_ELEM(Fctf, k, i, j) = A3D_ELEM(Ictf(), -kp, -ip, -jp);
+				}
+			}
+			else
+			{
+				CTF ctf;
+				ctf.setValues(DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_CTF_DEFOCUS_U),
+							  DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_CTF_DEFOCUS_V),
+							  DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_CTF_DEFOCUS_ANGLE),
+							  DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_CTF_VOLTAGE),
+							  DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_CTF_CS),
+							  DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_CTF_Q0),
+							  DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_CTF_BFAC));
+
+				ctf.getFftwImage(Fctf, mymodel.ori_size, mymodel.ori_size, mymodel.pixel_size,
+						ctf_phase_flipped, only_flip_phases, intact_ctf_first_peak, true);
+			}
 //#define DEBUG_CTF_FFTW_IMAGE
 #ifdef DEBUG_CTF_FFTW_IMAGE
-					Image<double> tt;
-					tt()=Fctf;
-					tt.write("relion_ctf.spi");
-					std::cerr << "Written relion_ctf.spi, now exiting..." << std::endl;
-					exit(1);
+			Image<DOUBLE> tt;
+			tt()=Fctf;
+			tt.write("relion_ctf.spi");
+			std::cerr << "Written relion_ctf.spi, now exiting..." << std::endl;
+			exit(1);
 #endif
 //#define DEBUG_GETCTF
 #ifdef DEBUG_GETCTF
-					std::cerr << " intact_ctf_first_peak= " << intact_ctf_first_peak << std::endl;
-					ctf.write(std::cerr);
-					Image<double> tmp;
-					tmp()=Fctf;
-					tmp.write("Fctf.spi");
-					tmp().resize(mymodel.ori_size, mymodel.ori_size);
-					ctf.getCenteredImage(tmp(), mymodel.pixel_size, ctf_phase_flipped, only_flip_phases, intact_ctf_first_peak, true);
-					tmp.write("Fctf_cen.spi");
-					std::cerr << "Written Fctf.spi, Fctf_cen.spi. Press any key to continue..." << std::endl;
-					char c;
-					std::cin >> c;
+			std::cerr << " intact_ctf_first_peak= " << intact_ctf_first_peak << std::endl;
+			ctf.write(std::cerr);
+			Image<DOUBLE> tmp;
+			tmp()=Fctf;
+			tmp.write("Fctf.spi");
+			tmp().resize(mymodel.ori_size, mymodel.ori_size);
+			ctf.getCenteredImage(tmp(), mymodel.pixel_size, ctf_phase_flipped, only_flip_phases, intact_ctf_first_peak, true);
+			tmp.write("Fctf_cen.spi");
+			std::cerr << "Written Fctf.spi, Fctf_cen.spi. Press any key to continue..." << std::endl;
+			char c;
+			std::cin >> c;
 #endif
-				}
-				else
-				{
-					Fctf.initConstant(1.);
-				}
-
-				// Store Fimg and Fctf
-				exp_Fimgs.at(my_image_no) = Fimg;
-				exp_Fctfs.at(my_image_no) = Fctf;
-
-			} // end loop iseries
-		}// end loop ipart
-	} // end while threadTaskDistributor
-
-	// All threads clear out their transformer object when they are finished
-	// This is to prevent a call from the first thread to fftw_cleanup, while there are still active plans in the transformer objects....
-	// The multi-threaded code with FFTW objects is really a bit of a pain...
-	if (thread_id != 0)
-	{
-		transformer.clear();
-	}
+		}
+		else
+		{
+			Fctf.initConstant(1.);
+		}
 
-	// Wait until all threads have finished
-	global_barrier->wait();
+		// Store Fimg and Fctf
+		exp_Fimgs.at(ipart) = Fimg;
+		exp_Fctfs.at(ipart) = Fctf;
 
-	// Only the first thread cleans up the fftw-junk in the transformer object
-	if (thread_id == 0)
-	{
-		transformer.cleanup();
-	}
+	} // end loop ipart
+	transformer.clear();
 
 }
 
-void MlOptimiser::doThreadPrecalculateShiftedImagesCtfsAndInvSigma2s(int thread_id)
+void MlOptimiser::precalculateShiftedImagesCtfsAndInvSigma2s(bool do_also_unmasked,
+		long int my_ori_particle, int exp_current_image_size, int exp_current_oversampling,
+		int exp_itrans_min, int exp_itrans_max,
+		std::vector<MultidimArray<Complex > > &exp_Fimgs,
+		std::vector<MultidimArray<Complex > > &exp_Fimgs_nomask,
+		std::vector<MultidimArray<DOUBLE> > &exp_Fctfs,
+		std::vector<MultidimArray<Complex > > &exp_local_Fimgs_shifted,
+		std::vector<MultidimArray<Complex > > &exp_local_Fimgs_shifted_nomask,
+		std::vector<MultidimArray<DOUBLE> > &exp_local_Fctfs,
+		std::vector<DOUBLE> &exp_local_sqrtXi2,
+		std::vector<MultidimArray<DOUBLE> > &exp_local_Minvsigma2s)
 {
+
 #ifdef TIMING
-	timer.tic(TIMING_DIFF_SHIFT);
+	if (my_ori_particle == exp_my_first_ori_particle)
+	{
+		if (do_also_unmasked)
+			timer.tic(TIMING_ESP_PRECW);
+		else if (exp_current_oversampling == 0) timer.tic(TIMING_ESP_PREC1);
+		else timer.tic(TIMING_ESP_PREC2);
+	}
 #endif
 
+	int exp_nr_particles = mydata.ori_particles[my_ori_particle].particles_id.size();
+	int nr_shifts = (do_shifts_onthefly || do_skip_align) ? exp_nr_particles : exp_nr_particles * sampling.NrTranslationalSamplings(exp_current_oversampling);
+	// Don't re-do if nothing has changed....
+	bool do_ctf_invsig = (exp_local_Fctfs.size() > 0) ? YSIZE(exp_local_Fctfs[0])  != exp_current_image_size : true; // size has changed
+	bool do_masked_shifts = (do_ctf_invsig || nr_shifts != exp_local_Fimgs_shifted.size()); // size or nr_shifts has changed
 
-	size_t first_ipart = 0, last_ipart = 0;
-	while (exp_ipart_ThreadTaskDistributor->getTasks(first_ipart, last_ipart))
+	// Use pre-sized vectors instead of push_backs!!
+	exp_local_Fimgs_shifted.resize(nr_shifts);
+	if (do_also_unmasked)
+		exp_local_Fimgs_shifted_nomask.resize(nr_shifts);
+	exp_local_Minvsigma2s.resize(exp_nr_particles);
+	exp_local_Fctfs.resize(exp_nr_particles);
+	exp_local_sqrtXi2.resize(exp_nr_particles);
+
+	MultidimArray<Complex > Fimg, Fimg_nomask;
+	for (int ipart = 0, my_trans_image = 0; ipart < mydata.ori_particles[my_ori_particle].particles_id.size(); ipart++)
 	{
-		for (long int ipart = first_ipart; ipart <= last_ipart; ipart++)
-		{
-			// the exp_ipart_ThreadTaskDistributor was set with nr_pool,
-			// but some, e.g. the last, batch of pooled particles may be smaller
-			if (ipart >= exp_nr_particles)
-				break;
-
-			long int part_id = exp_ipart_to_part_id[ipart];
-#ifdef DEBUG_CHECKSIZES
-			if (ipart >= exp_starting_image_no.size())
-			{
-				std::cerr<< "ipart= "<<ipart<<" exp_starting_image_no.size()= "<< exp_starting_image_no.size() <<std::endl;
-				REPORT_ERROR("ipart >= exp_starting_image_no.size()");
-			}
-#endif
-			int my_image_no = exp_starting_image_no[ipart] + exp_iseries;
-
-#ifdef DEBUG_CHECKSIZES
-			if (my_image_no >= exp_Fimgs.size())
-			{
-				std::cerr<< "my_image_no= "<<my_image_no<<" exp_Fimgs.size()= "<< exp_Fimgs.size() <<std::endl;
-				std::cerr << " exp_nr_trans= " << exp_nr_trans << " exp_nr_oversampled_trans= " << exp_nr_oversampled_trans << " exp_current_oversampling= " << exp_current_oversampling << std::endl;
-				REPORT_ERROR("my_image_no >= exp_Fimgs.size()");
-			}
-#endif
-			// Downsize Fimg and Fctf (again) to exp_current_image_size, also initialise Fref and Fimg_shift to the right size
-			MultidimArray<Complex > Fimg, Fshifted, Fimg_nomask, Fshifted_nomask;
-			windowFourierTransform(exp_Fimgs[my_image_no], Fimg, exp_current_image_size);
-			windowFourierTransform(exp_Fimgs_nomask[my_image_no], Fimg_nomask, exp_current_image_size);
+		long int part_id = mydata.ori_particles[my_ori_particle].particles_id[ipart];
+		int group_id = mydata.getGroupId(part_id);
 
+		if (do_masked_shifts)
+			windowFourierTransform(exp_Fimgs[ipart], Fimg, exp_current_image_size);
+		if (do_also_unmasked)
+			windowFourierTransform(exp_Fimgs_nomask[ipart], Fimg_nomask, exp_current_image_size);
 
+		if (do_ctf_invsig)
+		{
 			// Also precalculate the sqrt of the sum of all Xi2
-			// (Could exp_current_image_size ever be different from mymodel.current_size? Probhably therefore do it here rather than in getFourierTransforms
+			// Could exp_current_image_size ever be different from mymodel.current_size?
+			// Probably therefore do it here rather than in getFourierTransforms
 			if ((iter == 1 && do_firstiter_cc) || do_always_cc)
 			{
-				double sumxi2 = 0.;
+				DOUBLE sumxi2 = 0.;
 				FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Fimg)
 				{
 					sumxi2 += norm(DIRECT_MULTIDIM_ELEM(Fimg, n));
 				}
 				// Normalised cross-correlation coefficient: divide by power of reference (power of image is a constant)
-				exp_local_sqrtXi2[my_image_no] = sqrt(sumxi2);
-			}
-
-			// Store all translated variants of Fimg
-			int my_trans_image = my_image_no * exp_nr_trans * exp_nr_oversampled_trans;
-			for (long int itrans = 0; itrans < exp_nr_trans; itrans++)
-			{
-				// First get the non-oversampled translations as defined by the sampling object
-				std::vector<Matrix1D <double> > oversampled_translations;
-				sampling.getTranslations(itrans, exp_current_oversampling, oversampled_translations);
-
-#ifdef DEBUG_CHECKSIZES
-				if (oversampled_translations.size() != exp_nr_oversampled_trans)
-				{
-					std::cerr<< "oversampled_translations.size()= "<<oversampled_translations.size()<<" exp_nr_oversampled_trans= "<< exp_nr_oversampled_trans <<std::endl;
-					REPORT_ERROR("oversampled_translations.size() != exp_nr_oversampled_trans");
-				}
-#endif
-
-				// Then loop over all its oversampled relatives
-				for (long int iover_trans = 0; iover_trans < exp_nr_oversampled_trans; iover_trans++)
-				{
-//#define DEBUG_SHIFTS
-#ifdef DEBUG_SHIFTS
-					Image<double> It;
-					std::cerr << " iover_trans= " << iover_trans << " XX(oversampled_translations[iover_trans] )= " << XX(oversampled_translations[iover_trans] ) << " YY(oversampled_translations[iover_trans] )= " << YY(oversampled_translations[iover_trans] ) << std::endl;
-#endif
-					// Shift through phase-shifts in the Fourier transform
-					// Note that the shift search range is centered around (exp_old_xoff, exp_old_yoff)
-					shiftImageInFourierTransform(Fimg, Fshifted, tab_sin, tab_cos, (double)mymodel.ori_size, oversampled_translations[iover_trans]);
-					shiftImageInFourierTransform(Fimg_nomask, Fshifted_nomask, tab_sin, tab_cos, (double)mymodel.ori_size, oversampled_translations[iover_trans]);
-
-#ifdef DEBUG_SHIFTS
-					FourierTransformer transformer;
-					It().resize(YSIZE(Fimg), YSIZE(Fimg));
-					transformer.inverseFourierTransform(Fimg, It());
-					CenterFFT(It(), false);
-					It.write("Fimg.spi");
-					transformer.inverseFourierTransform(Fshifted, It());
-					CenterFFT(It(), false);
-					It.write("Fshifted.spi");
-					std::cerr << "Written Fimg and Fshifted, press any key to continue..." << std::endl;
-					char c;
-					std::cin >> c;
-#endif
-					// Store the shifted image
-					exp_local_Fimgs_shifted[my_trans_image] = Fshifted;
-					exp_local_Fimgs_shifted_nomask[my_trans_image] = Fshifted_nomask;
-					my_trans_image++;
-				}
+				exp_local_sqrtXi2[ipart] = sqrt(sumxi2);
 			}
 
-
 			// Also store downsized Fctfs
 			// In the second pass of the adaptive approach this will have no effect,
 			// since then exp_current_image_size will be the same as the size of exp_Fctfs
 #ifdef DEBUG_CHECKSIZES
-			if (my_image_no >= exp_Fctfs.size())
+			if (ipart >= exp_local_Fctfs.size())
 			{
-				std::cerr<< "my_image_no= "<<my_image_no<<" exp_Fctfs.size()= "<< exp_Fctfs.size() <<std::endl;
-				REPORT_ERROR("my_image_no >= exp_Fctfs.size()");
+				std::cerr<< "ipart= "<<ipart<<" exp_local_Fctfs.size()= "<< exp_local_Fctfs.size() <<std::endl;
+				REPORT_ERROR("ipart >= exp_local_Fctfs.size()");
 			}
 #endif
+			windowFourierTransform(exp_Fctfs[ipart], exp_local_Fctfs[ipart], exp_current_image_size);
 
-			MultidimArray<double> Fctf;
-			windowFourierTransform(exp_Fctfs[my_image_no], Fctf, exp_current_image_size);
-			exp_local_Fctfs[my_image_no] = Fctf;
-
-			// Get micrograph id (for choosing the right sigma2_noise)
-			int group_id = mydata.getGroupId(part_id, exp_iseries);
-
-			MultidimArray<double> Minvsigma2;
-			Minvsigma2.initZeros(YSIZE(Fimg), XSIZE(Fimg));
-			MultidimArray<int> * myMresol = (YSIZE(Fimg) == coarse_size) ? &Mresol_coarse : &Mresol_fine;
-
+			// Also prepare Minvsigma2
 #ifdef DEBUG_CHECKSIZES
-			if (!Minvsigma2.sameShape(*myMresol))
+			if (ipart >= exp_local_Minvsigma2s.size())
 			{
-				std::cerr<< "!Minvsigma2.sameShape(*myMresol)= "<<!Minvsigma2.sameShape(*myMresol) <<std::endl;
-				REPORT_ERROR("!Minvsigma2.sameShape(*myMresol)");
+				std::cerr<< "ipart= "<<ipart<<" exp_local_Minvsigma2s.size()= "<< exp_local_Minvsigma2s.size() <<std::endl;
+				REPORT_ERROR("ipart >= exp_local_Minvsigma2s.size()");
 			}
 #endif
+			if (mymodel.data_dim == 3)
+				exp_local_Minvsigma2s[ipart].initZeros(ZSIZE(Fimg), YSIZE(Fimg), XSIZE(Fimg));
+			else
+				exp_local_Minvsigma2s[ipart].initZeros(YSIZE(Fimg), XSIZE(Fimg));
+
+			int *myMresol = (YSIZE(Fimg) == coarse_size) ? Mresol_coarse.data : Mresol_fine.data;
 			// With group_id and relevant size of Fimg, calculate inverse of sigma^2 for relevant parts of Mresol
-			FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(*myMresol)
+			FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(exp_local_Minvsigma2s[ipart])
 			{
-				int ires = DIRECT_MULTIDIM_ELEM(*myMresol, n);
+				int ires = *(myMresol + n);
 				// Exclude origin (ires==0) from the Probability-calculation
 				// This way we are invariant to additive factors
 				if (ires > 0)
-				{
-					DIRECT_MULTIDIM_ELEM(Minvsigma2, n) = 1. / (sigma2_fudge * DIRECT_A1D_ELEM(mymodel.sigma2_noise[group_id], ires));
-				}
+					DIRECT_MULTIDIM_ELEM(exp_local_Minvsigma2s[ipart], n) = 1. / (sigma2_fudge * DIRECT_A1D_ELEM(mymodel.sigma2_noise[group_id], ires));
 			}
 
+		}
+
+		if (do_shifts_onthefly)
+		{
+			// Store a single, down-sized version of exp_Fimgs[ipart] in exp_local_Fimgs_shifted
 #ifdef DEBUG_CHECKSIZES
-			if (my_image_no >= exp_local_Minvsigma2s.size())
+			if (ipart >= exp_local_Fimgs_shifted.size())
 			{
-				std::cerr<< "my_image_no= "<<my_image_no<<" exp_local_Minvsigma2s.size()= "<< exp_local_Minvsigma2s.size() <<std::endl;
-				REPORT_ERROR("my_image_no >= exp_local_Minvsigma2s.size()");
+				std::cerr<< "ipart= "<<ipart<<" exp_local_Fimgs_shifted.size()= "<< exp_local_Fimgs_shifted.size() <<std::endl;
+				REPORT_ERROR("ipart >= exp_local_Fimgs_shifted.size()");
 			}
 #endif
-			exp_local_Minvsigma2s[my_image_no] = Minvsigma2;
+			if (do_masked_shifts)
+				exp_local_Fimgs_shifted[ipart] = Fimg;
+			if (do_also_unmasked)
+				exp_local_Fimgs_shifted_nomask[ipart] = Fimg_nomask;
+		}
+		else
+		{
+			// Store all translated variants of Fimg
+			for (long int itrans = exp_itrans_min; itrans <= exp_itrans_max; itrans++)
+			{
+				// First get the non-oversampled translations as defined by the sampling object
+				std::vector<DOUBLE > oversampled_translations_x, oversampled_translations_y, oversampled_translations_z;
+				sampling.getTranslations(itrans, exp_current_oversampling, oversampled_translations_x,
+						oversampled_translations_y, oversampled_translations_z);
+				// Then loop over all its oversampled relatives
+				for (long int iover_trans = 0; iover_trans < oversampled_translations_x.size(); iover_trans++, my_trans_image++)
+				{
+					// Shift through phase-shifts in the Fourier transform
+					// Note that the shift search range is centered around (exp_old_xoff, exp_old_yoff)
+					if (do_masked_shifts)
+					{
+						exp_local_Fimgs_shifted[my_trans_image].resize(Fimg);
+						if (mymodel.data_dim ==2)
+							shiftImageInFourierTransform(Fimg, exp_local_Fimgs_shifted[my_trans_image],
+									tab_sin, tab_cos, (DOUBLE)mymodel.ori_size,
+									oversampled_translations_x[iover_trans],
+									oversampled_translations_y[iover_trans]);
+						else
+							shiftImageInFourierTransform(Fimg, exp_local_Fimgs_shifted[my_trans_image],
+									tab_sin, tab_cos, (DOUBLE)mymodel.ori_size,
+									oversampled_translations_x[iover_trans],
+									oversampled_translations_y[iover_trans],
+									oversampled_translations_z[iover_trans]);
+					}
+					if (do_also_unmasked)
+					{
+						exp_local_Fimgs_shifted_nomask[my_trans_image].resize(Fimg_nomask);
+						if (mymodel.data_dim ==2)
+							shiftImageInFourierTransform(Fimg_nomask, exp_local_Fimgs_shifted_nomask[my_trans_image],
+								tab_sin, tab_cos, (DOUBLE)mymodel.ori_size,
+								oversampled_translations_x[iover_trans],
+								oversampled_translations_y[iover_trans]);
+						else
+							shiftImageInFourierTransform(Fimg_nomask, exp_local_Fimgs_shifted_nomask[my_trans_image],
+								tab_sin, tab_cos, (DOUBLE)mymodel.ori_size,
+								oversampled_translations_x[iover_trans],
+								oversampled_translations_y[iover_trans],
+								oversampled_translations_z[iover_trans]);
+					}
+				}
+			}
 		}
 	}
 
-	// Wait until all threads are finsished
-	global_barrier->wait();
-
 #ifdef TIMING
-	timer.toc(TIMING_DIFF_SHIFT);
+	if (my_ori_particle == exp_my_first_ori_particle)
+	{
+		if (do_also_unmasked)
+			timer.toc(TIMING_ESP_PRECW);
+		else if (exp_current_oversampling == 0) timer.toc(TIMING_ESP_PREC1);
+		else timer.toc(TIMING_ESP_PREC2);
+	}
 #endif
 
-
 }
 
-bool MlOptimiser::isSignificantAnyParticleAnyTranslation(long int iorient)
+bool MlOptimiser::isSignificantAnyParticleAnyTranslation(long int iorient, int exp_itrans_min, int exp_itrans_max, MultidimArray<bool> &exp_Mcoarse_significant)
 {
 
+	long int exp_nr_trans = exp_itrans_max - exp_itrans_min + 1;
 	for (long int ipart = 0; ipart < YSIZE(exp_Mcoarse_significant); ipart++)
 	{
 		long int ihidden = iorient * exp_nr_trans;
-		for (long int itrans = 0; itrans < exp_nr_trans; itrans++, ihidden++)
+		for (long int itrans = exp_itrans_min; itrans <= exp_itrans_max; itrans++, ihidden++)
 		{
 #ifdef DEBUG_CHECKSIZES
 			if (ihidden >= XSIZE(exp_Mcoarse_significant))
@@ -3127,866 +3313,798 @@ bool MlOptimiser::isSignificantAnyParticleAnyTranslation(long int iorient)
 
 }
 
-void MlOptimiser::doThreadGetSquaredDifferencesAllOrientations(int thread_id)
+
+void MlOptimiser::getAllSquaredDifferences(long int my_ori_particle, int exp_current_image_size,
+		int exp_ipass, int exp_current_oversampling, int metadata_offset,
+		int exp_idir_min, int exp_idir_max, int exp_ipsi_min, int exp_ipsi_max,
+		int exp_itrans_min, int exp_itrans_max, int exp_iclass_min, int exp_iclass_max,
+		std::vector<DOUBLE> &exp_min_diff2,
+		std::vector<DOUBLE> &exp_highres_Xi2_imgs,
+		std::vector<MultidimArray<Complex > > &exp_Fimgs,
+		std::vector<MultidimArray<DOUBLE> > &exp_Fctfs,
+		MultidimArray<DOUBLE> &exp_Mweight,
+		MultidimArray<bool> &exp_Mcoarse_significant,
+		std::vector<int> &exp_pointer_dir_nonzeroprior, std::vector<int> &exp_pointer_psi_nonzeroprior,
+		std::vector<DOUBLE> &exp_directions_prior, std::vector<DOUBLE> &exp_psi_prior,
+		std::vector<MultidimArray<Complex > > &exp_local_Fimgs_shifted,
+		std::vector<MultidimArray<DOUBLE> > &exp_local_Minvsigma2s,
+		std::vector<MultidimArray<DOUBLE> > &exp_local_Fctfs,
+		std::vector<DOUBLE> &exp_local_sqrtXi2)
 {
-#ifdef DEBUG_THREAD
-    std::cerr << "entering doThreadGetAllSquaredDifferences" << std::endl;
+
+#ifdef TIMING
+	if (my_ori_particle == exp_my_first_ori_particle)
+	{
+		if (exp_ipass == 0) timer.tic(TIMING_ESP_DIFF1);
+		else timer.tic(TIMING_ESP_DIFF2);
+	}
 #endif
 
-    // Local variables
-	std::vector<double> thisthread_min_diff2;
-    std::vector< Matrix1D<double> > oversampled_orientations, oversampled_translations;
-	MultidimArray<Complex > Fimg, Fref, Frefctf, Fimg_shift;
-	MultidimArray<double> Fctf, Minvsigma2;
-    Matrix2D<double> A;
+//#define DEBUG_GETALLDIFF2
+#ifdef DEBUG_GETALLDIFF2
+	std::cerr << " ipass= " << exp_ipass << " exp_current_oversampling= " << exp_current_oversampling << std::endl;
+	std::cerr << " sampling.NrPsiSamplings(exp_current_oversampling)= " << sampling.NrPsiSamplings(exp_current_oversampling) << std::endl;
+	std::cerr << " sampling.NrTranslationalSamplings(exp_current_oversampling)= " << sampling.NrTranslationalSamplings(exp_current_oversampling) << std::endl;
+	std::cerr << " sampling.NrSamplingPoints(exp_current_oversampling)= " << sampling.NrSamplingPoints(exp_current_oversampling) << std::endl;
+	std::cerr << " sampling.oversamplingFactorOrientations(exp_current_oversampling)= "<<sampling.oversamplingFactorOrientations(exp_current_oversampling) << std::endl;
+	std::cerr << " sampling.oversamplingFactorTranslations(exp_current_oversampling)= "<<sampling.oversamplingFactorTranslations(exp_current_oversampling) << std::endl;
+#endif
 
-    // Initialise local mindiff2 for thread-safety
-    thisthread_min_diff2.clear();
-    thisthread_min_diff2.resize(exp_nr_particles, 99.e99);
-    Fref.resize(exp_local_Fimgs_shifted[0]);
-	Frefctf.resize(exp_local_Fimgs_shifted[0]);
+	// Initialise min_diff and exp_Mweight for this pass
 
-    // THESE TWO FOR LOOPS WILL BE PARALLELISED USING THREADS...
-	// exp_iclass loop does not always go from 0 to nr_classes!
-	long int iorientclass_offset = exp_iclass * exp_nr_rot;
+	int exp_nr_particles = mydata.ori_particles[my_ori_particle].particles_id.size();
+	long int exp_nr_dir = (do_skip_align || do_skip_rotate) ? 1 : sampling.NrDirections(0, &exp_pointer_dir_nonzeroprior);
+	long int exp_nr_psi = (do_skip_align || do_skip_rotate) ? 1 : sampling.NrPsiSamplings(0, &exp_pointer_psi_nonzeroprior);
+	long int exp_nr_trans = (do_skip_align) ? 1 : sampling.NrTranslationalSamplings();
+	long int exp_nr_oversampled_rot = sampling.oversamplingFactorOrientations(exp_current_oversampling);
+	long int exp_nr_oversampled_trans = sampling.oversamplingFactorTranslations(exp_current_oversampling);
 
-    size_t first_iorient = 0, last_iorient = 0;
-	while (exp_iorient_ThreadTaskDistributor->getTasks(first_iorient, last_iorient))
-	{
-		for (long int iorient = first_iorient; iorient <= last_iorient; iorient++)
-		{
+	exp_Mweight.resize(exp_nr_particles, mymodel.nr_classes * exp_nr_dir * exp_nr_psi * exp_nr_trans * exp_nr_oversampled_rot * exp_nr_oversampled_trans);
+	exp_Mweight.initConstant(-999.);
+	if (exp_ipass==0)
+		exp_Mcoarse_significant.clear();
 
-			long int iorientclass = iorientclass_offset + iorient;
-			long int idir = iorient / exp_nr_psi;
-			long int ipsi = iorient % exp_nr_psi;
-			// Get prior for this direction and skip calculation if prior==0
-			double pdf_orientation;
-			if (mymodel.orientational_prior_mode == NOPRIOR)
-			{
-#ifdef DEBUG_CHECKSIZES
-				if (idir >= XSIZE(mymodel.pdf_direction[exp_iclass]))
-				{
-					std::cerr<< "idir= "<<idir<<" XSIZE(mymodel.pdf_direction[exp_iclass])= "<< XSIZE(mymodel.pdf_direction[exp_iclass]) <<std::endl;
-					REPORT_ERROR("idir >= mymodel.pdf_direction[exp_iclass].size()");
-				}
-#endif
-				pdf_orientation = DIRECT_MULTIDIM_ELEM(mymodel.pdf_direction[exp_iclass], idir);
-			}
-			else
-			{
-				pdf_orientation = sampling.getPriorProbability(idir, ipsi);
-			}
+	exp_min_diff2.clear();
+	exp_min_diff2.resize(exp_nr_particles, 99.e99);
 
-			// In the first pass, always proceed
-			// In the second pass, check whether one of the translations for this orientation of any of the particles had a significant weight in the first pass
-			// if so, proceed with projecting the reference in that direction
-			bool do_proceed = (exp_ipass==0) ? true : isSignificantAnyParticleAnyTranslation(iorientclass);
+	std::vector<MultidimArray<Complex > > dummy;
+	precalculateShiftedImagesCtfsAndInvSigma2s(false, my_ori_particle, exp_current_image_size, exp_current_oversampling,
+			exp_itrans_min, exp_itrans_max, exp_Fimgs, dummy, exp_Fctfs, exp_local_Fimgs_shifted, dummy,
+			exp_local_Fctfs, exp_local_sqrtXi2, exp_local_Minvsigma2s);
 
-			if (do_proceed && pdf_orientation > 0.)
+	// Loop only from exp_iclass_min to exp_iclass_max to deal with seed generation in first iteration
+	for (int exp_iclass = exp_iclass_min; exp_iclass <= exp_iclass_max; exp_iclass++)
+	{
+		if (mymodel.pdf_class[exp_iclass] > 0.)
+		{
+			// Local variables
+			std::vector< DOUBLE > oversampled_rot, oversampled_tilt, oversampled_psi;
+			std::vector< DOUBLE > oversampled_translations_x, oversampled_translations_y, oversampled_translations_z;
+			MultidimArray<Complex > Fimg, Fref, Frefctf, Fimg_otfshift;
+			DOUBLE *Minvsigma2;
+			Matrix2D<DOUBLE> A;
+
+			Fref.resize(exp_local_Minvsigma2s[0]);
+			Frefctf.resize(exp_local_Minvsigma2s[0]);
+			if (do_shifts_onthefly)
+				Fimg_otfshift.resize(Frefctf);
+
+            for (long int idir = exp_idir_min, iorient = 0; idir <= exp_idir_max; idir++)
 			{
-				// Now get the oversampled (rot, tilt, psi) triplets
-				// This will be only the original (rot,tilt,psi) triplet in the first pass (exp_current_oversampling==0)
-				sampling.getOrientations(idir, ipsi, exp_current_oversampling, oversampled_orientations);
+				for (long int ipsi = exp_ipsi_min; ipsi <= exp_ipsi_max; ipsi++, iorient++)
+				{
+					long int iorientclass = exp_iclass * exp_nr_dir * exp_nr_psi + iorient;
 
+					// Get prior for this direction and skip calculation if prior==0
+					DOUBLE pdf_orientation;
+					if (do_skip_align || do_skip_rotate)
+					{
+						#ifdef DEBUG_CHECKSIZES
+						if (exp_iclass >= mymodel.pdf_class.size())
+						{
+							std::cerr<< "exp_iclass= "<<exp_iclass<<" mymodel.pdf_class.size()= "<< mymodel.pdf_class.size() <<std::endl;
+							REPORT_ERROR("exp_iclass >= mymodel.pdf_class.size()");
+						}
+						#endif
+						pdf_orientation = mymodel.pdf_class[exp_iclass];
+					}
+					else if (mymodel.orientational_prior_mode == NOPRIOR)
+					{
 #ifdef DEBUG_CHECKSIZES
-				if (exp_nr_oversampled_rot != oversampled_orientations.size())
-				{
-					std::cerr<< "exp_nr_oversampled_rot= "<<exp_nr_oversampled_rot<<" oversampled_orientations.size()= "<< oversampled_orientations.size() <<std::endl;
-					REPORT_ERROR("exp_nr_oversampled_rot != oversampled_orientations.size()");
-				}
+						if (idir >= XSIZE(mymodel.pdf_direction[exp_iclass]))
+						{
+							std::cerr<< "idir= "<<idir<<" XSIZE(mymodel.pdf_direction[exp_iclass])= "<< XSIZE(mymodel.pdf_direction[exp_iclass]) <<std::endl;
+							REPORT_ERROR("idir >= mymodel.pdf_direction[exp_iclass].size()");
+						}
 #endif
-				// Loop over all oversampled orientations (only a single one in the first pass)
-				for (long int iover_rot = 0; iover_rot < exp_nr_oversampled_rot; iover_rot++)
-				{
-
-					// Get the Euler matrix
-					Euler_angles2matrix(XX(oversampled_orientations[iover_rot]),
-										YY(oversampled_orientations[iover_rot]),
-										ZZ(oversampled_orientations[iover_rot]), A);
-
-					// Take tilt-series into account
-					A = (exp_R_mic * A).inv();
-
-					// Project the reference map (into Fref)
+						pdf_orientation = DIRECT_MULTIDIM_ELEM(mymodel.pdf_direction[exp_iclass], idir);
+					}
+					else
+					{
+						pdf_orientation = exp_directions_prior[idir] * exp_psi_prior[ipsi];
+					}
+					// In the first pass, always proceed
+					// In the second pass, check whether one of the translations for this orientation of any of the particles had a significant weight in the first pass
+					// if so, proceed with projecting the reference in that direction
+					bool do_proceed = (exp_ipass==0) ? true :
+						isSignificantAnyParticleAnyTranslation(iorientclass, exp_itrans_min, exp_itrans_max, exp_Mcoarse_significant);
+					if (do_proceed && pdf_orientation > 0.)
+					{
+						// Now get the oversampled (rot, tilt, psi) triplets
+						// This will be only the original (rot,tilt,psi) triplet in the first pass (exp_current_oversampling==0)
+						sampling.getOrientations(idir, ipsi, exp_current_oversampling, oversampled_rot, oversampled_tilt, oversampled_psi,
+								exp_pointer_dir_nonzeroprior, exp_directions_prior, exp_pointer_psi_nonzeroprior, exp_psi_prior);
+						// Loop over all oversampled orientations (only a single one in the first pass)
+						for (long int iover_rot = 0; iover_rot < exp_nr_oversampled_rot; iover_rot++)
+						{
+							// Get the Euler matrix
+							Euler_angles2matrix(oversampled_rot[iover_rot],
+												oversampled_tilt[iover_rot],
+												oversampled_psi[iover_rot], A);
+							// Project the reference map (into Fref)
 #ifdef TIMING
-					// Only time one thread, as I also only time one MPI process
-					if (thread_id == 0)
-						timer.tic(TIMING_DIFF_PROJ);
+							// Only time one thread, as I also only time one MPI process
+							if (my_ori_particle == exp_my_first_ori_particle)
+								timer.tic(TIMING_DIFF_PROJ);
 #endif
-					(mymodel.PPref[exp_iclass]).get2DFourierTransform(Fref, A, IS_INV);
+							(mymodel.PPref[exp_iclass]).get2DFourierTransform(Fref, A, IS_NOT_INV);
 #ifdef TIMING
-					// Only time one thread, as I also only time one MPI process
-					if (thread_id == 0)
-						timer.toc(TIMING_DIFF_PROJ);
-#endif
-
-					/// Now that reference projection has been made loop over someParticles!
-					for (long int ori_part_id = exp_my_first_ori_particle, ipart = 0; ori_part_id <= exp_my_last_ori_particle; ori_part_id++)
-					{
-						// loop over all particles inside this ori_particle
-						for (long int i = 0; i < mydata.ori_particles[ori_part_id].particles_id.size(); i++, ipart++)
-						{
-							long int part_id = mydata.ori_particles[ori_part_id].particles_id[i];
-
-							bool is_last_image_in_series = mydata.getNrImagesInSeries(part_id) == (exp_iseries + 1);
-							// Which number was this image in the combined array of exp_iseries and part_id
-							long int my_image_no = exp_starting_image_no[ipart] + exp_iseries;
-
-#ifdef DEBUG_CHECKSIZES
-							if (my_image_no >= exp_local_Minvsigma2s.size())
-							{
-								std::cerr<< "my_image_no= "<<my_image_no<<" exp_local_Minvsigma2s.size()= "<< exp_local_Minvsigma2s.size() <<std::endl;
-								REPORT_ERROR("my_image_no >= exp_local_Minvsigma2.size()");
-							}
+							// Only time one thread, as I also only time one MPI process
+							if (my_ori_particle == exp_my_first_ori_particle)
+								timer.toc(TIMING_DIFF_PROJ);
 #endif
-							Minvsigma2 = exp_local_Minvsigma2s[my_image_no];
-
-							// Apply CTF to reference projection
-							if (do_ctf_correction && refs_are_ctf_corrected)
+							/// Now that reference projection has been made loop over someParticles!
+							// loop over all particles inside this ori_particle
+							for (long int ipart = 0; ipart < mydata.ori_particles[my_ori_particle].particles_id.size(); ipart++)
 							{
-
 #ifdef DEBUG_CHECKSIZES
-								if (my_image_no >= exp_local_Fctfs.size())
+								if (my_ori_particle >= mydata.ori_particles.size())
 								{
-									std::cerr<< "my_image_no= "<<my_image_no<<" exp_local_Fctfs.size()= "<< exp_local_Fctfs.size() <<std::endl;
-									REPORT_ERROR("my_image_no >= exp_local_Fctfs.size()");
+									std::cerr<< "my_ori_particle= "<<my_ori_particle<<" mydata.ori_particles.size()= "<< mydata.ori_particles.size() <<std::endl;
+									REPORT_ERROR("my_ori_particle >= mydata.ori_particles.size()");
 								}
-								if (MULTIDIM_SIZE(Fref) != MULTIDIM_SIZE(exp_local_Fctfs[my_image_no]))
+								if (ipart >= mydata.ori_particles[my_ori_particle].particles_id.size())
 								{
-									std::cerr<< "MULTIDIM_SIZE(Fref)= "<<MULTIDIM_SIZE(Fref)<<" MULTIDIM_SIZE()= "<< MULTIDIM_SIZE(exp_local_Fctfs[my_image_no]) <<std::endl;
-									REPORT_ERROR("MULTIDIM_SIZE(Fref) != MULTIDIM_SIZE(exp_local_Fctfs[my_image_no)");
+									std::cerr<< "ipart= "<<ipart<<" mydata.ori_particles[my_ori_particle].particles_id.size()= "<< mydata.ori_particles[my_ori_particle].particles_id.size() <<std::endl;
+									REPORT_ERROR("ipart >= mydata.ori_particles[my_ori_particle].particles_id.size()");
 								}
-
 #endif
-								FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Fref)
-								{
-									DIRECT_MULTIDIM_ELEM(Frefctf, n) = DIRECT_MULTIDIM_ELEM(Fref, n) * DIRECT_MULTIDIM_ELEM(exp_local_Fctfs[my_image_no], n);
-								}
-							}
-							else
-								Frefctf = Fref;
-
-							if (do_scale_correction)
-							{
-								int group_id = mydata.getGroupId(part_id, exp_iseries);
+								long int part_id = mydata.ori_particles[my_ori_particle].particles_id[ipart];
 #ifdef DEBUG_CHECKSIZES
-								if (group_id >= mymodel.scale_correction.size())
+								if (ipart >= exp_local_Minvsigma2s.size())
 								{
-									std::cerr<< "group_id= "<<group_id<<" mymodel.scale_correction.size()= "<< mymodel.scale_correction.size() <<std::endl;
-									REPORT_ERROR("group_id >= mymodel.scale_correction.size()");
+									std::cerr<< "ipart= "<<ipart<<" exp_local_Minvsigma2s.size()= "<< exp_local_Minvsigma2s.size() <<std::endl;
+									REPORT_ERROR("ipart >= exp_local_Minvsigma2s.size()");
 								}
 #endif
-								double myscale = mymodel.scale_correction[group_id];
-								FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Frefctf)
+								Minvsigma2 = exp_local_Minvsigma2s[ipart].data;
+
+								// Apply CTF to reference projection
+								if (do_ctf_correction && refs_are_ctf_corrected)
 								{
-									DIRECT_MULTIDIM_ELEM(Frefctf, n) *= myscale;
+									FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Fref)
+									{
+										DIRECT_MULTIDIM_ELEM(Frefctf, n) = DIRECT_MULTIDIM_ELEM(Fref, n) * DIRECT_MULTIDIM_ELEM(exp_local_Fctfs[ipart], n);
+									}
 								}
-							}
-
-							for (long int itrans = 0; itrans < exp_nr_trans; itrans++)
-							{
-								long int ihidden = iorientclass * exp_nr_trans + itrans;
+								else
+									Frefctf = Fref;
 
-#ifdef DEBUG_CHECKSIZES
-								if (exp_ipass > 0 && ihidden >= XSIZE(exp_Mcoarse_significant))
+								if (do_scale_correction)
 								{
-									std::cerr<< "ihidden= "<<ihidden<<" XSIZE(exp_Mcoarse_significant)= "<< XSIZE(exp_Mcoarse_significant) <<std::endl;
-									REPORT_ERROR("ihidden >= XSIZE(exp_Mcoarse_significant)");
-								}
+									int group_id = mydata.getGroupId(part_id);
+#ifdef DEBUG_CHECKSIZES
+									if (group_id >= mymodel.scale_correction.size())
+									{
+										std::cerr<< "group_id= "<<group_id<<" mymodel.scale_correction.size()= "<< mymodel.scale_correction.size() <<std::endl;
+										REPORT_ERROR("group_id >= mymodel.scale_correction.size()");
+									}
 #endif
-								// In the first pass, always proceed
-								// In the second pass, check whether this translations (&orientation) had a significant weight in the first pass
-								bool do_proceed = (exp_ipass == 0) ? true : DIRECT_A2D_ELEM(exp_Mcoarse_significant, ipart, ihidden);
-								if (do_proceed)
-								{
+									DOUBLE myscale = mymodel.scale_correction[group_id];
+									FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Frefctf)
+									{
+										DIRECT_MULTIDIM_ELEM(Frefctf, n) *= myscale;
+									}
+								}
 
-									sampling.getTranslations(itrans, exp_current_oversampling, oversampled_translations);
-									for (long int iover_trans = 0; iover_trans < exp_nr_oversampled_trans; iover_trans++)
+								long int ihidden = iorientclass * exp_nr_trans;
+								for (long int itrans = exp_itrans_min; itrans <= exp_itrans_max; itrans++, ihidden++)
+								{
+#ifdef DEBUG_CHECKSIZES
+									if (exp_ipass > 0 && ihidden >= XSIZE(exp_Mcoarse_significant))
 									{
+										std::cerr<< "ihidden= "<<ihidden<<" XSIZE(exp_Mcoarse_significant)= "<< XSIZE(exp_Mcoarse_significant) <<std::endl;
+										REPORT_ERROR("ihidden >= XSIZE(exp_Mcoarse_significant)");
+									}
+#endif
+									// In the first pass, always proceed
+									// In the second pass, check whether this translations (&orientation) had a significant weight in the first pass
+									bool do_proceed = (exp_ipass == 0) ? true : DIRECT_A2D_ELEM(exp_Mcoarse_significant, ipart, ihidden);
+									if (do_proceed)
+									{
+										sampling.getTranslations(itrans, exp_current_oversampling,
+												oversampled_translations_x, oversampled_translations_y, oversampled_translations_z );
+										for (long int iover_trans = 0; iover_trans < exp_nr_oversampled_trans; iover_trans++)
+										{
 #ifdef TIMING
-										// Only time one thread, as I also only time one MPI process
-										if (thread_id == 0)
-											timer.tic(TIMING_DIFF_DIFF2);
+											// Only time one thread, as I also only time one MPI process
+											if (my_ori_particle == exp_my_first_ori_particle)
+												timer.tic(TIMING_DIFF2_GETSHIFT);
 #endif
-										// Get the shifted image
-										long int ishift = my_image_no * exp_nr_oversampled_trans * exp_nr_trans +
-												itrans * exp_nr_oversampled_trans + iover_trans;
-
+											/// Now get the shifted image
+											// Use a pointer to avoid copying the entire array again in this highly expensive loop
+											Complex *Fimg_shift;
+											if (!do_shifts_onthefly)
+											{
+												long int ishift = ipart * exp_nr_oversampled_trans * exp_nr_trans +
+														(itrans - exp_itrans_min) * exp_nr_oversampled_trans + iover_trans;
+												if (do_skip_align)
+													ishift = ipart;
 #ifdef DEBUG_CHECKSIZES
-										if (ishift >= exp_local_Fimgs_shifted.size())
-										{
-											std::cerr<< "ishift= "<<ishift<<" exp_local_Fimgs_shifted.size()= "<< exp_local_Fimgs_shifted.size() <<std::endl;
-											std::cerr << " itrans= " << itrans << std::endl;
-											std::cerr << " ipart= " << ipart << std::endl;
-											std::cerr << " exp_nr_oversampled_trans= " << exp_nr_oversampled_trans << " exp_nr_trans= " << exp_nr_trans << " iover_trans= " << iover_trans << std::endl;
-											REPORT_ERROR("ishift >= exp_local_Fimgs_shifted.size()");
-										}
+												if (ishift >= exp_local_Fimgs_shifted.size())
+												{
+													std::cerr<< "ishift= "<<ishift<<" exp_local_Fimgs_shifted.size()= "<< exp_local_Fimgs_shifted.size() <<std::endl;
+													std::cerr << " itrans= " << itrans << std::endl;
+													std::cerr << " ipart= " << ipart << std::endl;
+													std::cerr << " exp_nr_oversampled_trans= " << exp_nr_oversampled_trans << " exp_nr_trans= " << exp_nr_trans << " iover_trans= " << iover_trans << std::endl;
+													REPORT_ERROR("ishift >= exp_local_Fimgs_shifted.size()");
+												}
+#endif
+												Fimg_shift = exp_local_Fimgs_shifted[ishift].data;
+											}
+											else
+											{
+
+												// Calculate shifted image on-the-fly to save replicating memory in multi-threaded jobs.
+												Complex *myAB;
+												if (exp_current_oversampling == 0)
+												{
+													#ifdef DEBUG_CHECKSIZES
+													if (YSIZE(Frefctf) == coarse_size && itrans >= global_fftshifts_ab_coarse.size())
+													{
+														std::cerr<< "itrans= "<<itrans<<" global_fftshifts_ab_coarse.size()= "<< global_fftshifts_ab_coarse.size() <<std::endl;
+														REPORT_ERROR("itrans >= global_fftshifts_ab_coarse.size()");
+													}
+													if (YSIZE(Frefctf) != coarse_size && itrans >= global_fftshifts_ab_current.size())
+													{
+														std::cerr<< "itrans= "<<itrans<<" global_fftshifts_ab_current.size()= "<< global_fftshifts_ab_current.size() <<std::endl;
+														REPORT_ERROR("itrans >= global_fftshifts_ab_current.size()");
+													}
+													#endif
+													myAB = (YSIZE(Frefctf) == coarse_size) ? global_fftshifts_ab_coarse[itrans].data
+													        : global_fftshifts_ab_current[itrans].data;
+												}
+												else
+												{
+													int iitrans = itrans * exp_nr_oversampled_trans +  iover_trans;
+													myAB = (strict_highres_exp > 0.) ? global_fftshifts_ab2_coarse[iitrans].data
+															: global_fftshifts_ab2_current[iitrans].data;
+												}
+												FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(exp_local_Fimgs_shifted[ipart])
+												{
+													DOUBLE real = (*(myAB + n)).real * (DIRECT_MULTIDIM_ELEM(exp_local_Fimgs_shifted[ipart], n)).real
+															- (*(myAB + n)).imag *(DIRECT_MULTIDIM_ELEM(exp_local_Fimgs_shifted[ipart], n)).imag;
+													DOUBLE imag = (*(myAB + n)).real * (DIRECT_MULTIDIM_ELEM(exp_local_Fimgs_shifted[ipart], n)).imag
+															+ (*(myAB + n)).imag *(DIRECT_MULTIDIM_ELEM(exp_local_Fimgs_shifted[ipart], n)).real;
+													DIRECT_MULTIDIM_ELEM(Fimg_otfshift, n) = Complex(real, imag);
+												}
+												Fimg_shift = Fimg_otfshift.data;
+											}
+#ifdef TIMING
+											// Only time one thread, as I also only time one MPI process
+											if (my_ori_particle == exp_my_first_ori_particle)
+												timer.toc(TIMING_DIFF2_GETSHIFT);
 #endif
 
-										Fimg_shift = exp_local_Fimgs_shifted[ishift];
 //#define DEBUG_GETALLDIFF2
 #ifdef DEBUG_GETALLDIFF2
-                                        if (verb> 0)
-                                        {
-										FourierTransformer transformer;
-										Image<double> tt;
-										tt().resize(exp_current_image_size, exp_current_image_size);
-										transformer.inverseFourierTransform(Fimg_shift, tt());
-										CenterFFT(tt(),false);
-										tt.write("Fimg_shift.spi");
-										transformer.inverseFourierTransform(Frefctf, tt());
-										CenterFFT(tt(),false);
-										tt.write("Fref.spi");
-										tt()=Minvsigma2;
-										tt.write("Minvsigma2.spi");
-										std::cerr << "written Minvsigma2.spi" << std::endl;
-
-										char c;
-										std::cerr << "Written Fimg_shift.spi and Fref.spi. Press any key to continue..." << std::endl;
-										std::cin >> c;
-										exit(1);
-                                        }
-#endif
-
-										double diff2;
-										if ((iter == 1 && do_firstiter_cc) || do_always_cc)
-										{
-											// Do not calculate squared-differences, but signal product
-											// Negative values because smaller is worse in this case
-											diff2 = 0.;
-											double suma2 = 0.;
-											FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Fimg_shift)
+											pthread_mutex_lock(&global_mutex);
+											//if (verb> 0)
 											{
-												diff2 -= (DIRECT_MULTIDIM_ELEM(Frefctf, n)).real * (DIRECT_MULTIDIM_ELEM(Fimg_shift, n)).real;
-												diff2 -= (DIRECT_MULTIDIM_ELEM(Frefctf, n)).imag * (DIRECT_MULTIDIM_ELEM(Fimg_shift, n)).imag;
-												suma2 += norm(DIRECT_MULTIDIM_ELEM(Frefctf, n));
-											}
-											// Normalised cross-correlation coefficient: divide by power of reference (power of image is a constant)
-											diff2 /= sqrt(suma2) * exp_local_sqrtXi2[my_image_no];
-										}
-										else
-										{
+											std::cerr << " A= " << A << std::endl;
 
-#ifdef DEBUG_CHECKSIZES
-											if (my_image_no >= exp_highres_Xi2_imgs.size())
+											FourierTransformer transformer;
+											MultidimArray<Complex> Fish;
+											Fish.resize(exp_local_Minvsigma2s[0]);
+											FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Fish)
 											{
-												std::cerr<< "my_image_no= "<<my_image_no<<" exp_highres_Xi2_imgs.size()= "<< exp_highres_Xi2_imgs.size() <<std::endl;
-												REPORT_ERROR("my_image_no >= exp_highres_Xi2_imgs.size()");
+												DIRECT_MULTIDIM_ELEM(Fish, n) = *(Fimg_shift + n);
 											}
-#endif
+											Image<DOUBLE> tt;
+											if (mymodel.data_dim == 3)
+												tt().resize(exp_current_image_size, exp_current_image_size, exp_current_image_size);
+											else
+												tt().resize(exp_current_image_size, exp_current_image_size);
+											transformer.inverseFourierTransform(Fish, tt());
+											CenterFFT(tt(),false);
+											tt.write("Fimg_shift.spi");
 
-											// Calculate the actual squared difference term of the Gaussian probability function
-											// If current_size < mymodel.ori_size diff2 is initialised to the sum of
-											// all |Xij|2 terms that lie between current_size and ori_size
-											// Factor two because of factor 2 in division below, NOT because of 2-dimensionality of the complex plane!
-											diff2 = exp_highres_Xi2_imgs[my_image_no] / 2.;
-											FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Fimg_shift)
-											{
-												double diff_real = (DIRECT_MULTIDIM_ELEM(Frefctf, n)).real - (DIRECT_MULTIDIM_ELEM(Fimg_shift, n)).real;
-												double diff_imag = (DIRECT_MULTIDIM_ELEM(Frefctf, n)).imag - (DIRECT_MULTIDIM_ELEM(Fimg_shift, n)).imag;
-												diff2 += (diff_real * diff_real + diff_imag * diff_imag) * 0.5 * DIRECT_MULTIDIM_ELEM(Minvsigma2, n);
+											transformer.inverseFourierTransform(Frefctf, tt());
+											CenterFFT(tt(),false);
+											tt.write("Fref.spi");
+											char c;
+											std::cerr << " ipart " << ipart << " DIRECT_MULTIDIM_ELEM(exp_local_Fctfs[ipart], 12)= " << DIRECT_MULTIDIM_ELEM(exp_local_Fctfs[ipart], 12) << std::endl;
+											std::cerr << " ipart " << ipart << " DIRECT_MULTIDIM_ELEM(exp_Fctfs[ipart], 12)= " << DIRECT_MULTIDIM_ELEM(exp_Fctfs[ipart], 12) << std::endl;
+
+											int group_id = mydata.getGroupId(part_id);
+											DOUBLE myscale = mymodel.scale_correction[group_id];
+											std::cerr << " oversampled_rot[iover_rot]= " << oversampled_rot[iover_rot] << " oversampled_tilt[iover_rot]= " << oversampled_tilt[iover_rot] << " oversampled_psi[iover_rot]= " << oversampled_psi[iover_rot] << std::endl;
+											std::cerr << " group_id= " << group_id << " myscale= " << myscale <<std::endl;
+											std::cerr << " itrans= " << itrans << " itrans * exp_nr_oversampled_trans +  iover_trans= " << itrans * exp_nr_oversampled_trans +  iover_trans << " ihidden= " << ihidden << std::endl;
+											std::cerr << "Written Fimg_shift.spi and Fref.spi. Press any key to continue... my_ori_particle= " << my_ori_particle<< std::endl;
+											std::cin >> c;
 											}
+											pthread_mutex_unlock(&global_mutex);
 
-										}
-
+#endif
 #ifdef TIMING
-										// Only time one thread, as I also only time one MPI process
-										if (thread_id == 0)
-											timer.toc(TIMING_DIFF_DIFF2);
+											// Only time one thread, as I also only time one MPI process
+											if (my_ori_particle == exp_my_first_ori_particle)
+												timer.tic(TIMING_DIFF_DIFF2);
+#endif
+											DOUBLE diff2;
+											if ((iter == 1 && do_firstiter_cc) || do_always_cc)
+											{
+												// Do not calculate squared-differences, but signal product
+												// Negative values because smaller is worse in this case
+												diff2 = 0.;
+												DOUBLE suma2 = 0.;
+												FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Frefctf)
+												{
+													diff2 -= (DIRECT_MULTIDIM_ELEM(Frefctf, n)).real * (*(Fimg_shift + n)).real;
+												    diff2 -= (DIRECT_MULTIDIM_ELEM(Frefctf, n)).imag * (*(Fimg_shift + n)).imag;
+													suma2 += norm(DIRECT_MULTIDIM_ELEM(Frefctf, n));
+												}
+												// Normalised cross-correlation coefficient: divide by power of reference (power of image is a constant)
+												diff2 /= sqrt(suma2) * exp_local_sqrtXi2[ipart];
+											}
+											else
+											{
+#ifdef DEBUG_CHECKSIZES
+												if (ipart >= exp_highres_Xi2_imgs.size())
+												{
+													std::cerr<< "ipart= "<<ipart<<" exp_highres_Xi2_imgs.size()= "<< exp_highres_Xi2_imgs.size() <<std::endl;
+													REPORT_ERROR("ipart >= exp_highres_Xi2_imgs.size()");
+												}
+#endif
+												// Calculate the actual squared difference term of the Gaussian probability function
+												// If current_size < mymodel.ori_size diff2 is initialised to the sum of
+												// all |Xij|2 terms that lie between current_size and ori_size
+												// Factor two because of factor 2 in division below, NOT because of 2-dimensionality of the complex plane!
+												diff2 = exp_highres_Xi2_imgs[ipart] / 2.;
+												FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Frefctf)
+												{
+													DOUBLE diff_real = (DIRECT_MULTIDIM_ELEM(Frefctf, n)).real - (*(Fimg_shift + n)).real;
+													DOUBLE diff_imag = (DIRECT_MULTIDIM_ELEM(Frefctf, n)).imag - (*(Fimg_shift + n)).imag;
+													diff2 += (diff_real * diff_real + diff_imag * diff_imag) * 0.5 * (*(Minvsigma2 + n));
+												}
+											}
+#ifdef TIMING
+											// Only time one thread, as I also only time one MPI process
+											if (my_ori_particle == exp_my_first_ori_particle)
+												timer.toc(TIMING_DIFF_DIFF2);
 #endif
 
-										// Store all diff2 in exp_Mweight
-										long int ihidden_over = sampling.getPositionOversampledSamplingPoint(ihidden, exp_current_oversampling,
-																										iover_rot, iover_trans);
-
+											// Store all diff2 in exp_Mweight
+											long int ihidden_over = sampling.getPositionOversampledSamplingPoint(ihidden, exp_current_oversampling,
+																											iover_rot, iover_trans);
 //#define DEBUG_DIFF2_ISNAN
 #ifdef DEBUG_DIFF2_ISNAN
-										if (std::isnan(diff2))
-										{
-											global_mutex.lock();
-											std::cerr << " ipart= " << ipart << std::endl;
-											std::cerr << " diff2= " << diff2 << " thisthread_min_diff2[ipart]= " << thisthread_min_diff2[ipart] << " ipart= " << ipart << std::endl;
-											std::cerr << " exp_highres_Xi2_imgs[my_image_no]= " << exp_highres_Xi2_imgs[my_image_no] << std::endl;
-											std::cerr<< " exp_nr_oversampled_trans="<<exp_nr_oversampled_trans<<std::endl;
-											std::cerr<< " exp_nr_oversampled_rot="<<exp_nr_oversampled_rot<<std::endl;
-											std::cerr << " iover_rot= " << iover_rot << " iover_trans= " << iover_trans << " ihidden= " << ihidden << std::endl;
-											std::cerr << " exp_current_oversampling= " << exp_current_oversampling << std::endl;
-											std::cerr << " ihidden_over= " << ihidden_over << " XSIZE(Mweight)= " << XSIZE(exp_Mweight) << std::endl;
-											int group_id = mydata.getGroupId(part_id, exp_iseries);
-											std::cerr << " mymodel.scale_correction[group_id]= " << mymodel.scale_correction[group_id] << std::endl;
-											if (std::isnan(mymodel.scale_correction[group_id]))
+											if (std::isnan(diff2))
 											{
-												for (int i=0; i < mymodel.scale_correction.size(); i++)
-													std::cerr << " i= " << i << " mymodel.scale_correction[i]= " << mymodel.scale_correction[i] << std::endl;
+												pthread_mutex_lock(&global_mutex);
+												std::cerr << " ipart= " << ipart << std::endl;
+												std::cerr << " diff2= " << diff2 << " thisthread_min_diff2[ipart]= " << thisthread_min_diff2[ipart] << " ipart= " << ipart << std::endl;
+												std::cerr << " exp_highres_Xi2_imgs[ipart]= " << exp_highres_Xi2_imgs[ipart] << std::endl;
+												std::cerr<< " exp_nr_oversampled_trans="<<exp_nr_oversampled_trans<<std::endl;
+												std::cerr<< " exp_nr_oversampled_rot="<<exp_nr_oversampled_rot<<std::endl;
+												std::cerr << " iover_rot= " << iover_rot << " iover_trans= " << iover_trans << " ihidden= " << ihidden << std::endl;
+												std::cerr << " exp_current_oversampling= " << exp_current_oversampling << std::endl;
+												std::cerr << " ihidden_over= " << ihidden_over << " XSIZE(Mweight)= " << XSIZE(exp_Mweight) << std::endl;
+												int group_id = mydata.getGroupId(part_id);
+												std::cerr << " mymodel.scale_correction[group_id]= " << mymodel.scale_correction[group_id] << std::endl;
+												if (std::isnan(mymodel.scale_correction[group_id]))
+												{
+													for (int i=0; i < mymodel.scale_correction.size(); i++)
+														std::cerr << " i= " << i << " mymodel.scale_correction[i]= " << mymodel.scale_correction[i] << std::endl;
+												}
+												std::cerr << " group_id= " << group_id << std::endl;
+												Image<DOUBLE> It;
+												std::cerr << "Frefctf shape= "; Frefctf.printShape(std::cerr);
+												std::cerr << "Fimg_shift shape= "; Fimg_shift.printShape(std::cerr);
+												It()=exp_local_Fctfs[ipart];
+												It.write("exp_local_Fctf.spi");
+												std::cerr << "written exp_local_Fctf.spi" << std::endl;
+												FourierTransformer transformer;
+												Image<DOUBLE> tt;
+												tt().resize(exp_current_image_size, exp_current_image_size);
+												transformer.inverseFourierTransform(Fimg_shift, tt());
+												CenterFFT(tt(),false);
+												tt.write("Fimg_shift.spi");
+												std::cerr << "written Fimg_shift.spi" << std::endl;
+												FourierTransformer transformer2;
+												tt().initZeros();
+												transformer2.inverseFourierTransform(Frefctf, tt());
+												CenterFFT(tt(),false);
+												tt.write("Frefctf.spi");
+												std::cerr << "written Frefctf.spi" << std::endl;
+												FourierTransformer transformer3;
+												tt().initZeros();
+												transformer3.inverseFourierTransform(Fref, tt());
+												CenterFFT(tt(),false);
+												tt.write("Fref.spi");
+												std::cerr << "written Fref.spi" << std::endl;
+												std::cerr << " A= " << A << std::endl;
+												std::cerr << " exp_R_mic= " << exp_R_mic << std::endl;
+												std::cerr << "written Frefctf.spi" << std::endl;
+												REPORT_ERROR("diff2 is not a number");
+												pthread_mutex_unlock(&global_mutex);
 											}
-											std::cerr << " group_id= " << group_id << std::endl;
-											Image<double> It;
-											It()=Minvsigma2;
-											It.write("Minvsigma2.spi");
-											std::cerr << "written Minvsigma2.spi" << std::endl;
-											std::cerr << "Frefctf shape= "; Frefctf.printShape(std::cerr);
-											std::cerr << "Fimg_shift shape= "; Fimg_shift.printShape(std::cerr);
-											It()=exp_local_Fctfs[my_image_no];
-											It.write("exp_local_Fctf.spi");
-											std::cerr << "written exp_local_Fctf.spi" << std::endl;
-											FourierTransformer transformer;
-											Image<double> tt;
-											tt().resize(exp_current_image_size, exp_current_image_size);
-											transformer.inverseFourierTransform(Fimg_shift, tt());
-											CenterFFT(tt(),false);
-											tt.write("Fimg_shift.spi");
-											std::cerr << "written Fimg_shift.spi" << std::endl;
-											FourierTransformer transformer2;
-											tt().initZeros();
-											transformer2.inverseFourierTransform(Frefctf, tt());
-											CenterFFT(tt(),false);
-											tt.write("Frefctf.spi");
-											std::cerr << "written Frefctf.spi" << std::endl;
-											FourierTransformer transformer3;
-											tt().initZeros();
-											transformer3.inverseFourierTransform(Fref, tt());
-											CenterFFT(tt(),false);
-											tt.write("Fref.spi");
-											std::cerr << "written Fref.spi" << std::endl;
-											std::cerr << " A= " << A << std::endl;
-											std::cerr << " exp_R_mic= " << exp_R_mic << std::endl;
-											std::cerr << "written Frefctf.spi" << std::endl;
-											REPORT_ERROR("diff2 is not a number");
-											global_mutex.unlock();
-										}
 #endif
 //#define DEBUG_VERBOSE
 #ifdef DEBUG_VERBOSE
-										global_mutex.lock();
-										if (verb > 0)
-										{
-											std::cout << " rot= " << XX(oversampled_orientations[iover_rot]) << " tilt= "<< YY(oversampled_orientations[iover_rot]) << " psi= " << ZZ(oversampled_orientations[iover_rot]) << std::endl;
-											std::cout << " xoff= " <<XX(oversampled_translations[iover_trans]) <<" yoff= "<<YY(oversampled_translations[iover_trans])<<std::endl;
-											std::cout << " ihidden_over= " << ihidden_over << " diff2= " << diff2 << " thisthread_min_diff2[ipart]= " << thisthread_min_diff2[ipart] << std::endl;
-										}
-										global_mutex.unlock();
+											pthread_mutex_lock(&global_mutex);
+											if (verb > 0)
+											{
+												std::cout << " rot= " << oversampled_rot[iover_rot] << " tilt= "<< oversampled_tilt[iover_rot] << " psi= " << oversampled_psi[iover_rot] << std::endl;
+												std::cout << " xoff= " <<oversampled_translations_x[iover_trans]) <<" yoff= "<<oversampled_translations_y[iover_trans])<<std::endl;
+												std::cout << " ihidden_over= " << ihidden_over << " diff2= " << diff2 << " thisthread_min_diff2[ipart]= " << thisthread_min_diff2[ipart] << std::endl;
+											}
+											pthread_mutex_unlock(&global_mutex);
 #endif
 #ifdef DEBUG_CHECKSIZES
-										if (ihidden_over >= XSIZE(exp_Mweight) )
-										{
-											std::cerr<< " exp_nr_oversampled_trans="<<exp_nr_oversampled_trans<<std::endl;
-											std::cerr<< " exp_nr_oversampled_rot="<<exp_nr_oversampled_rot<<std::endl;
-											std::cerr << " iover_rot= " << iover_rot << " iover_trans= " << iover_trans << " ihidden= " << ihidden << std::endl;
-											std::cerr << " exp_current_oversampling= " << exp_current_oversampling << std::endl;
-											std::cerr << " ihidden_over= " << ihidden_over << " XSIZE(Mweight)= " << XSIZE(exp_Mweight) << std::endl;
-											REPORT_ERROR("ihidden_over >= XSIZE(Mweight)");
-										}
+											if (ihidden_over >= XSIZE(exp_Mweight) )
+											{
+												std::cerr<< " exp_nr_oversampled_trans="<<exp_nr_oversampled_trans<<std::endl;
+												std::cerr<< " exp_nr_oversampled_rot="<<exp_nr_oversampled_rot<<std::endl;
+												std::cerr << " iover_rot= " << iover_rot << " iover_trans= " << iover_trans << " ihidden= " << ihidden << std::endl;
+												std::cerr << " exp_current_oversampling= " << exp_current_oversampling << std::endl;
+												std::cerr << " exp_itrans_min= " << exp_itrans_min <<" exp_nr_trans= " << exp_nr_trans << std::endl;
+												std::cerr << " exp_itrans_max= " << exp_itrans_max << " iorientclass= " << iorientclass << " itrans= " << itrans << std::endl;
+												std::cerr << " exp_nr_dir= " << exp_nr_dir << " exp_idir_min= " << exp_idir_min << " exp_idir_max= " << exp_idir_max << std::endl;
+												std::cerr << " exp_nr_psi= " << exp_nr_psi << " exp_ipsi_min= " << exp_ipsi_min << " exp_ipsi_max= " << exp_ipsi_max << std::endl;
+												std::cerr << " exp_iclass= " << exp_iclass << " exp_iclass_min= " << exp_iclass_min << " exp_iclass_max= " << exp_iclass_max << std::endl;
+												std::cerr << " iorient= " << iorient << std::endl;
+												std::cerr << " ihidden_over= " << ihidden_over << " XSIZE(Mweight)= " << XSIZE(exp_Mweight) << std::endl;
+												REPORT_ERROR("ihidden_over >= XSIZE(Mweight)");
+											}
 #endif
-
-										if (exp_iseries == 0)
+											//std::cerr << " my_ori_particle= " << my_ori_particle<< " ipart= " << ipart << " ihidden_over= " << ihidden_over << " diff2= " << diff2 << std::endl;
 											DIRECT_A2D_ELEM(exp_Mweight, ipart, ihidden_over) = diff2;
-										else
-											DIRECT_A2D_ELEM(exp_Mweight, ipart, ihidden_over) += diff2;
-
 #ifdef DEBUG_CHECKSIZES
-										if (ipart >= thisthread_min_diff2.size())
-										{
-											std::cerr<< "ipart= "<<ipart<<" thisthread_min_diff2.size()= "<< thisthread_min_diff2.size() <<std::endl;
-											REPORT_ERROR("ipart >= thisthread_min_diff2.size() ");
-										}
-#endif
-										// Keep track of minimum of all diff2, only for the last image in this series
-										diff2 = DIRECT_A2D_ELEM(exp_Mweight, ipart, ihidden_over);
-										//std::cerr << " exp_ipass= " << exp_ipass << " exp_iclass= " << exp_iclass << " diff2= " << diff2 << std::endl;
-										if (is_last_image_in_series && diff2 < thisthread_min_diff2[ipart])
-											thisthread_min_diff2[ipart] = diff2;
-
-									} // end loop iover_trans
-								} // end if do_proceed translations
-							} // end loop itrans
-						} // end loop part_id (i)
-					} // end loop ori_part_id
-				}// end loop iover_rot
-			} // end if do_proceed orientations
-		} // end loop iorient
-	} // end while task distribution
-
-
-	// Now inside a mutex set the minimum of the squared differences among all threads
-#ifdef DEBUG_CHECKSIZES
-	if (thisthread_min_diff2.size() != exp_min_diff2.size())
-	{
-		std::cerr<< "thisthread_min_diff2.size()= "<<thisthread_min_diff2.size()<<" exp_min_diff2.size()= "<< exp_min_diff2.size() <<std::endl;
-		REPORT_ERROR("thisthread_min_diff2.size() != exp_min_diff2.size()");
-	}
+											if (ipart >= exp_min_diff2.size())
+											{
+												std::cerr<< "ipart= "<<ipart<<" exp_min_diff2.size()= "<< exp_min_diff2.size() <<std::endl;
+												REPORT_ERROR("ipart >= exp_min_diff2.size() ");
+											}
 #endif
+											// Keep track of minimum of all diff2, only for the last image in this series
+											if (diff2 < exp_min_diff2[ipart])
+												exp_min_diff2[ipart] = diff2;
+
+										} // end loop iover_trans
+									} // end if do_proceed translations
+								} // end loop itrans
+							} // end loop part_id
+						}// end loop iover_rot
+					} // end if do_proceed orientations
+				} // end loop ipsi
+			} // end loop idir
+		} // end if mymodel.pdf_class[iclass] > 0.
+	} // end loop iclass
 
-	global_mutex.lock();
-	for (int i = 0; i < exp_min_diff2.size(); i++)
+#ifdef TIMING
+	if (my_ori_particle == exp_my_first_ori_particle)
 	{
-		if (thisthread_min_diff2[i] < exp_min_diff2[i])
-		{
-			exp_min_diff2[i] = thisthread_min_diff2[i];
-		}
+		if (exp_ipass == 0) timer.toc(TIMING_ESP_DIFF1);
+		else timer.toc(TIMING_ESP_DIFF2);
 	}
-	global_mutex.unlock();
-
-	// Wait until all threads have finished
-	global_barrier->wait();
-
-#ifdef DEBUG_THREAD
-    std::cerr << "leaving doThreadGetAllSquaredDifferences" << std::endl;
 #endif
 
 }
 
 
-void MlOptimiser::getAllSquaredDifferences()
+void MlOptimiser::convertAllSquaredDifferencesToWeights(long int my_ori_particle, int exp_ipass,
+		int exp_current_oversampling, int metadata_offset,
+		int exp_idir_min, int exp_idir_max, int exp_ipsi_min, int exp_ipsi_max,
+		int exp_itrans_min, int exp_itrans_max, int exp_iclass_min, int exp_iclass_max,
+		MultidimArray<DOUBLE> &exp_Mweight, MultidimArray<bool> &exp_Mcoarse_significant,
+		std::vector<DOUBLE> &exp_significant_weight, std::vector<DOUBLE> &exp_sum_weight,
+		std::vector<Matrix1D<DOUBLE> > &exp_old_offset, std::vector<Matrix1D<DOUBLE> > &exp_prior,
+		std::vector<DOUBLE> &exp_min_diff2,
+		std::vector<int> &exp_pointer_dir_nonzeroprior, std::vector<int> &exp_pointer_psi_nonzeroprior,
+		std::vector<DOUBLE> &exp_directions_prior, std::vector<DOUBLE> &exp_psi_prior)
 {
 
 #ifdef TIMING
-	if (exp_ipass == 0) timer.tic(TIMING_ESP_DIFF1);
-	else timer.tic(TIMING_ESP_DIFF2);
-#endif
-
-//#define DEBUG_GETALLDIFF2
-#ifdef DEBUG_GETALLDIFF2
-	std::cerr << " ipass= " << exp_ipass << " exp_current_oversampling= " << exp_current_oversampling << std::endl;
-	std::cerr << " sampling.NrPsiSamplings(exp_current_oversampling)= " << sampling.NrPsiSamplings(exp_current_oversampling) << std::endl;
-	std::cerr << " sampling.NrTranslationalSamplings(exp_current_oversampling)= " << sampling.NrTranslationalSamplings(exp_current_oversampling) << std::endl;
-	std::cerr << " sampling.NrSamplingPoints(exp_current_oversampling)= " << sampling.NrSamplingPoints(exp_current_oversampling) << std::endl;
-	std::cerr << " sampling.oversamplingFactorOrientations(exp_current_oversampling)= "<<sampling.oversamplingFactorOrientations(exp_current_oversampling) << std::endl;
-	std::cerr << " sampling.oversamplingFactorTranslations(exp_current_oversampling)= "<<sampling.oversamplingFactorTranslations(exp_current_oversampling) << std::endl;
-#endif
-
-	// Initialise min_diff and exp_Mweight for this pass
-	exp_Mweight.resize(exp_nr_particles, mymodel.nr_classes * sampling.NrSamplingPoints(exp_current_oversampling, false));
-	exp_Mweight.initConstant(-999.);
-	if (exp_ipass==0)
-		exp_Mcoarse_significant.clear();
-
-	exp_min_diff2.clear();
-	exp_min_diff2.resize(exp_nr_particles);
-	for (int n = 0; n < exp_nr_particles; n++)
-		exp_min_diff2[n] = 99.e99;
-
-	// Use pre-sized vectors instead of push_backs!!
-	exp_local_Fimgs_shifted.clear();
-	exp_local_Fimgs_shifted.resize(exp_nr_images * sampling.NrTranslationalSamplings(exp_current_oversampling));
-	exp_local_Fimgs_shifted_nomask.clear();
-	exp_local_Fimgs_shifted_nomask.resize(exp_nr_images * sampling.NrTranslationalSamplings(exp_current_oversampling));
-	exp_local_Minvsigma2s.clear();
-	exp_local_Minvsigma2s.resize(exp_nr_images);
-	exp_local_Fctfs.clear();
-	exp_local_Fctfs.resize(exp_nr_images);
-	exp_local_sqrtXi2.clear();
-	exp_local_sqrtXi2.resize(exp_nr_images);
-
-	// TODO: MAKE SURE THAT ALL PARTICLES IN SomeParticles ARE FROM THE SAME AREA, SO THAT THE R_mic CAN BE RE_USED!!!
-
-	//for (exp_iseries = 0; exp_iseries < mydata.getNrImagesInSeries(part_id); exp_iseries++)
-	for (exp_iseries = 0; exp_iseries < mydata.getNrImagesInSeries((mydata.ori_particles[exp_my_first_ori_particle]).particles_id[0]); exp_iseries++)
+	if (my_ori_particle == exp_my_first_ori_particle)
 	{
-
-		// Get all shifted versions of the (downsized) images, their (downsized) CTFs and their inverted Sigma2 matrices
-		exp_ipart_ThreadTaskDistributor->reset(); // reset thread distribution tasks
-		global_ThreadManager->run(globalThreadPrecalculateShiftedImagesCtfsAndInvSigma2s);
-
-		// Get micrograph transformation matrix. Note that for all pooled particles (exp_my_first_particle-exp_my_last_particle)
-		// the same exp_R_mic will be used in order to re-use the reference projections
-		// This is the reason why all pooled particles should come from the same micrograph
-		// TODO: THAT STILL NEEDS TO BE CONFIRMED!!!! CURRENTLY NO CHECK ON SAME-PARTICLENAME IN EACH POOL!!!
-		// WORKAROUND FOR NOW: just set --pool 1
-		//exp_R_mic = mydata.getMicrographTransformationMatrix((mydata.ori_particles[exp_my_first_ori_particle]).particles_id[0], exp_iseries);
-		int my_image_no = exp_starting_image_no[0] + exp_iseries;
-		// Get micrograph transformation matrix
-		exp_R_mic.resize(3,3);
-		exp_R_mic(0,0) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_0_0);
-		exp_R_mic(0,1) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_0_1);
-		exp_R_mic(0,2) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_0_2);
-		exp_R_mic(1,0) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_1_0);
-		exp_R_mic(1,1) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_1_1);
-		exp_R_mic(1,2) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_1_2);
-		exp_R_mic(2,0) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_2_0);
-		exp_R_mic(2,1) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_2_1);
-		exp_R_mic(2,2) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_2_2);
-
-		// Loop from iclass_min to iclass_max to deal with seed generation in first iteration
-		for (exp_iclass = iclass_min; exp_iclass <= iclass_max; exp_iclass++)
-		{
-			if (mymodel.pdf_class[exp_iclass] > 0.)
-			{
-
-				exp_iorient_ThreadTaskDistributor->reset(); // reset thread distribution tasks
-				global_ThreadManager->run(globalThreadGetSquaredDifferencesAllOrientations);
-
-			} // end if mymodel.pdf_class[iclass] > 0.
-		} // end loop iclass
-	} // end loop iseries
-
-#ifdef DEBUG_GETALLDIFF2b
-	for (long int part_id = exp_my_first_particle, ipart = 0; part_id <= exp_my_last_particle; part_id++, ipart++)
-	{
-		if (exp_min_diff2[ipart] < 0.)
-		{
-			std::cerr << "Negative min_diff2...." << std::endl;
-			std::cerr << " ipart= " << ipart << " part_id= "<<part_id<<std::endl;
-			std::cerr << " do_firstiter_cc= " << do_firstiter_cc << std::endl;
-			int group_id = mydata.getGroupId(part_id, 0);
-			std::cerr << " group_id= " << group_id << std::endl;
-			std::cerr << " ml_model.sigma2_noise[group_id]= " << mymodel.sigma2_noise[group_id] << std::endl;
-		}
+		if (exp_ipass == 0) timer.tic(TIMING_ESP_WEIGHT1);
+		else timer.tic(TIMING_ESP_WEIGHT2);
 	}
 #endif
 
-#ifdef TIMING
-	if (exp_ipass == 0) timer.toc(TIMING_ESP_DIFF1);
-	else timer.toc(TIMING_ESP_DIFF2);
-#endif
+	// Convert the squared differences into weights
+	// Note there is only one weight for each part_id, because a whole series of images is treated as one particle
 
-}
+	long int exp_nr_dir = (do_skip_align || do_skip_rotate) ? 1 : sampling.NrDirections(0, &exp_pointer_dir_nonzeroprior);
+	long int exp_nr_psi = (do_skip_align || do_skip_rotate) ? 1 : sampling.NrPsiSamplings(0, &exp_pointer_psi_nonzeroprior);
+	long int exp_nr_trans = (do_skip_align) ? 1 : sampling.NrTranslationalSamplings();
+	long int exp_nr_particles = mydata.ori_particles[my_ori_particle].particles_id.size();
+	long int exp_nr_oversampled_rot = sampling.oversamplingFactorOrientations(exp_current_oversampling);
+	long int exp_nr_oversampled_trans = sampling.oversamplingFactorTranslations(exp_current_oversampling);
 
-void MlOptimiser::doThreadConvertSquaredDifferencesToWeightsAllOrientations(int thread_id)
-{
-#ifdef DEBUG_THREAD
-    std::cerr << "entering doThreadConvertSquaredDifferencesToWeightsAllOrientations" << std::endl;
-#endif
+	// Initialising...
+	exp_sum_weight.clear();
+	exp_sum_weight.resize(exp_nr_particles, 0.);
 
+//#define DEBUG_CONVERTDIFF2W
+#ifdef DEBUG_CONVERTDIFF2W
+	DOUBLE max_weight = -1.;
+	DOUBLE opt_psi, opt_xoff, opt_yoff;
+	int opt_iover_rot, opt_iover_trans, opt_ipsi, opt_itrans;
+	long int opt_ihidden, opt_ihidden_over;
+#endif
 
-    // Store local sum of weights for this thread and then combined all threads at the end of this function inside a mutex.
-	double thisthread_sumweight = 0.;
+	// loop over all particles inside this ori_particle
+	for (long int ipart = 0; ipart < exp_nr_particles; ipart++)
+	{
+		long int part_id = mydata.ori_particles[my_ori_particle].particles_id[ipart];
+		DOUBLE exp_thisparticle_sumweight = 0.;
 
-	// exp_iclass loop does not always go from 0 to nr_classes!
-	long int iorientclass_offset = exp_iclass * exp_nr_rot;
+		DOUBLE old_offset_z;
+		DOUBLE old_offset_x = XX(exp_old_offset[ipart]);
+		DOUBLE old_offset_y = YY(exp_old_offset[ipart]);
+		if (mymodel.data_dim == 3)
+			old_offset_z = ZZ(exp_old_offset[ipart]);
 
-	size_t first_iorient = 0, last_iorient = 0;
-	while (exp_iorient_ThreadTaskDistributor->getTasks(first_iorient, last_iorient))
-	{
-		for (long int iorient = first_iorient; iorient <= last_iorient; iorient++)
+		if ((iter == 1 && do_firstiter_cc) || do_always_cc)
 		{
+			// Binarize the squared differences array to skip marginalisation
+			DOUBLE mymindiff2 = 99.e10;
+			long int myminidx = -1;
+			// Find the smallest element in this row of exp_Mweight
+			for (long int i = 0; i < XSIZE(exp_Mweight); i++)
+			{
 
-			double pdf_orientation;
-			long int iorientclass = iorientclass_offset + iorient;
-			long int idir = iorient / exp_nr_psi;
-			long int ipsi = iorient % exp_nr_psi;
+				DOUBLE cc = DIRECT_A2D_ELEM(exp_Mweight, ipart, i);
+				// ignore non-determined cc
+				if (cc == -999.)
+					continue;
 
-			// Get prior for this direction
-			if (mymodel.orientational_prior_mode == NOPRIOR)
-			{
-#ifdef DEBUG_CHECKSIZES
-				if (idir >= XSIZE(mymodel.pdf_direction[exp_iclass]))
+				// just search for the maximum
+				if (cc < mymindiff2)
 				{
-					std::cerr<< "idir= "<<idir<<" XSIZE(mymodel.pdf_direction[exp_iclass])= "<< XSIZE(mymodel.pdf_direction[exp_iclass]) <<std::endl;
-					REPORT_ERROR("idir >= mymodel.pdf_direction[exp_iclass].size()");
+					mymindiff2 = cc;
+					myminidx = i;
 				}
-#endif
-				pdf_orientation = DIRECT_MULTIDIM_ELEM(mymodel.pdf_direction[exp_iclass], idir);
-			}
-			else
-			{
-				pdf_orientation = sampling.getPriorProbability(idir, ipsi);
 			}
+			// Set all except for the best hidden variable to zero and the smallest element to 1
+			for (long int i = 0; i < XSIZE(exp_Mweight); i++)
+				DIRECT_A2D_ELEM(exp_Mweight, ipart, i)= 0.;
 
-			// Loop over all translations
-			for (long int itrans = 0; itrans < exp_nr_trans; itrans++)
-			{
-
-				long int ihidden = iorientclass * exp_nr_trans + itrans;
+			DIRECT_A2D_ELEM(exp_Mweight, ipart, myminidx)= 1.;
+			exp_thisparticle_sumweight += 1.;
 
-				// To speed things up, only calculate pdf_offset at the coarse sampling.
-				// That should not matter much, and that way one does not need to calculate all the OversampledTranslations
-				Matrix1D<double> my_offset, my_prior;
-				sampling.getTranslation(itrans, my_offset);
-				// Convert offsets back to Angstroms to calculate PDF!
-				// TODO: if series, then have different exp_old_xoff for each my_image_no....
-				// WHAT TO DO WITH THIS?!!!
+		}
+		else
+		{
+			// Loop from iclass_min to iclass_max to deal with seed generation in first iteration
+			for (int exp_iclass = exp_iclass_min; exp_iclass <= exp_iclass_max; exp_iclass++)
+			{
 
-				double pdf_offset;
+				// Make PdfOffset calculation much faster...
+				DOUBLE myprior_x, myprior_y, myprior_z;
 				if (mymodel.ref_dim == 2)
-					pdf_offset = calculatePdfOffset(exp_old_offset[exp_iimage] + my_offset, mymodel.prior_offset_class[exp_iclass]);
+				{
+					myprior_x = XX(mymodel.prior_offset_class[exp_iclass]);
+					myprior_y = YY(mymodel.prior_offset_class[exp_iclass]);
+				}
 				else
-					pdf_offset = calculatePdfOffset(exp_old_offset[exp_iimage] + my_offset, exp_prior[exp_iimage]);
-
-				// TMP DEBUGGING
-				if (mymodel.orientational_prior_mode != NOPRIOR && (pdf_offset==0. || pdf_orientation==0.))
 				{
-					global_mutex.lock();
-					std::cerr << " pdf_offset= " << pdf_offset << " pdf_orientation= " << pdf_orientation << std::endl;
-					std::cerr << " exp_ipart= " << exp_ipart << " exp_part_id= " << exp_part_id << std::endl;
-					std::cerr << " iorient= " << iorient << " idir= " << idir << " ipsi= " << ipsi << std::endl;
-					std::cerr << " exp_nr_psi= " << exp_nr_psi << " exp_nr_dir= " << exp_nr_dir << " exp_nr_trans= " << exp_nr_trans << std::endl;
-					for (long int i = 0; i < sampling.directions_prior.size(); i++)
-						std::cerr << " sampling.directions_prior["<<i<<"]= " << sampling.directions_prior[i] << std::endl;
-					for (long int i = 0; i < sampling.psi_prior.size(); i++)
-						std::cerr << " sampling.psi_prior["<<i<<"]= " << sampling.psi_prior[i] << std::endl;
-					REPORT_ERROR("ERROR! pdf_offset==0.|| pdf_orientation==0.");
-					global_mutex.unlock();
+					myprior_x = XX(exp_prior[ipart]);
+					myprior_y = YY(exp_prior[ipart]);
+					if (mymodel.data_dim == 3)
+						myprior_z = ZZ(exp_prior[ipart]);
 				}
-				if (exp_nr_oversampled_rot == 0)
-					REPORT_ERROR("exp_nr_oversampled_rot == 0");
-				if (exp_nr_oversampled_trans == 0)
-					REPORT_ERROR("exp_nr_oversampled_trans == 0");
-
-
-#ifdef TIMING
-				// Only time one thread, as I also only time one MPI process
-				if (thread_id == 0)
-					timer.tic(TIMING_WEIGHT_EXP);
-#endif
-
-				// Now first loop over iover_rot, because that is the order in exp_Mweight as well
-				long int ihidden_over = ihidden * exp_nr_oversampled_rot * exp_nr_oversampled_trans;
-				for (long int iover_rot = 0; iover_rot < exp_nr_oversampled_rot; iover_rot++)
+				for (long int idir = exp_idir_min, iorient = 0; idir <= exp_idir_max; idir++)
 				{
-					// Then loop over iover_trans
-					for (long int iover_trans = 0; iover_trans < exp_nr_oversampled_trans; iover_trans++, ihidden_over++)
+					for (long int ipsi = exp_ipsi_min; ipsi <= exp_ipsi_max; ipsi++, iorient++)
 					{
+						long int iorientclass = exp_iclass * exp_nr_dir * exp_nr_psi + iorient;
+						DOUBLE pdf_orientation;
 
-#ifdef DEBUG_CHECKSIZES
-						if (ihidden_over >= XSIZE(exp_Mweight))
+						// Get prior for this direction
+						if (do_skip_align || do_skip_rotate)
 						{
-							std::cerr<< "ihidden_over= "<<ihidden_over<<" XSIZE(Mweight)= "<< XSIZE(exp_Mweight) <<std::endl;
-							REPORT_ERROR("ihidden_over >= XSIZE(exp_Mweight)");
+							pdf_orientation = mymodel.pdf_class[exp_iclass];
 						}
-#endif
-
-						// Only exponentiate for determined values of exp_Mweight
-						// (this is always true in the first pass, but not so in the second pass)
-						// Only deal with this sampling point if its weight was significant
-#ifdef DEBUG_CHECKSIZES
-						if (exp_iimage >= YSIZE(exp_Mweight))
+						else if (mymodel.orientational_prior_mode == NOPRIOR)
 						{
-							std::cerr<< "exp_iimage= "<<exp_iimage<<" YSIZE(exp_Mweight)= "<< YSIZE(exp_Mweight) <<std::endl;
-							std::cerr << " exp_ipart= " << exp_ipart << std::endl;
-							std::cerr << " DIRECT_A2D_ELEM(exp_Mweight, exp_iimage, ihidden_over)= " << DIRECT_A2D_ELEM(exp_Mweight, exp_iimage, ihidden_over) << std::endl;
-							std::cerr << " DIRECT_A2D_ELEM(exp_Mweight, exp_ipart, ihidden_over)= " << DIRECT_A2D_ELEM(exp_Mweight, exp_ipart, ihidden_over) << std::endl;
-							REPORT_ERROR("exp_iimage >= YSIZE(exp_Mweight)");
-						}
+#ifdef DEBUG_CHECKSIZES
+							if (idir >= XSIZE(mymodel.pdf_direction[exp_iclass]))
+							{
+								std::cerr<< "idir= "<<idir<<" XSIZE(mymodel.pdf_direction[exp_iclass])= "<< XSIZE(mymodel.pdf_direction[exp_iclass]) <<std::endl;
+								REPORT_ERROR("idir >= mymodel.pdf_direction[exp_iclass].size()");
+							}
 #endif
-
-						if (DIRECT_A2D_ELEM(exp_Mweight, exp_ipart, ihidden_over) < 0.)
-						{
-							DIRECT_A2D_ELEM(exp_Mweight, exp_ipart, ihidden_over) = 0.;
+							pdf_orientation = DIRECT_MULTIDIM_ELEM(mymodel.pdf_direction[exp_iclass], idir);
 						}
 						else
 						{
-							double weight = pdf_orientation * pdf_offset;
-
-							double diff2 = DIRECT_A2D_ELEM(exp_Mweight, exp_ipart, ihidden_over) - exp_min_diff2[exp_ipart];
-
-							// next line because of numerical precision of exp-function
-							if (diff2 > 700.) weight = 0.;
-							// TODO: use tabulated exp function?
-							else weight *= exp(-diff2);
-//#define DEBUG_PSIANGLE_PDISTRIBUTION
-#ifdef DEBUG_PSIANGLE_PDISTRIBUTION
-							std::cout << ipsi*360./sampling.NrPsiSamplings() << " "<< weight << std::endl;
-#endif
-							// Store the weight
-							DIRECT_A2D_ELEM(exp_Mweight, exp_ipart, ihidden_over) = weight;
+							pdf_orientation = exp_directions_prior[idir] * exp_psi_prior[ipsi];
+						}
+						// Loop over all translations
+						long int ihidden = iorientclass * exp_nr_trans;
+						for (long int itrans = exp_itrans_min; itrans <= exp_itrans_max; itrans++, ihidden++)
+						{
 
-#ifdef DEBUG_CHECKSIZES
-							if (std::isnan(weight))
+							// To speed things up, only calculate pdf_offset at the coarse sampling.
+							// That should not matter much, and that way one does not need to calculate all the OversampledTranslations
+							DOUBLE offset_x = old_offset_x + sampling.translations_x[itrans];
+							DOUBLE offset_y = old_offset_y + sampling.translations_y[itrans];
+							DOUBLE tdiff2 = (offset_x - myprior_x) * (offset_x - myprior_x) + (offset_y - myprior_y) * (offset_y - myprior_y);
+							if (mymodel.data_dim == 3)
 							{
-								global_mutex.lock();
-								std::cerr<< "weight= "<<weight<<" is not a number! " <<std::endl;
-								std::cerr << " exp_min_diff2[exp_ipart]= " << exp_min_diff2[exp_ipart] << std::endl;
-								std::cerr << " exp_ipart= " << exp_ipart << std::endl;
-								std::cerr << " exp_part_id= " << exp_part_id << std::endl;
-								std::cerr << " mydata.getNrImagesInSeries(exp_part_id)= " << mydata.getNrImagesInSeries(exp_part_id) << std::endl;
-								long int my_image_no = exp_iimage + 0;
-								std::cerr << " my_image_no= " << my_image_no << std::endl;
-								std::cerr << " exp_iimage= " << exp_iimage << std::endl;
-								std::cerr << " DIRECT_A2D_ELEM(exp_Mweight, my_image_no, ihidden_over)= " << DIRECT_A2D_ELEM(exp_Mweight, my_image_no, ihidden_over) << std::endl;
-								REPORT_ERROR("weight is not a number");
-								global_mutex.unlock();
+								DOUBLE offset_z = old_offset_z + sampling.translations_z[itrans];
+								tdiff2 += (offset_z - myprior_z) * (offset_z - myprior_z);
 							}
-#endif
-
-							// Keep track of sum and maximum of all weights for this particle
-							// Later add all to exp_thisparticle_sumweight, but inside this loop sum to local thisthread_sumweight first
-							thisthread_sumweight += weight;
+							DOUBLE pdf_offset;
+							if (mymodel.sigma2_offset < 0.0001)
+								pdf_offset = ( tdiff2 > 0.) ? 0. : 1.;
+							else
+								pdf_offset = exp ( tdiff2 / (-2. * mymodel.sigma2_offset) ) / ( 2. * PI * mymodel.sigma2_offset );
 
-						} // end if/else exp_Mweight < 0.
-					} // end loop iover_trans
-				}// end loop iover_rot
+							// TMP DEBUGGING
+							if (mymodel.orientational_prior_mode != NOPRIOR && (pdf_offset==0. || pdf_orientation==0.))
+							{
+								pthread_mutex_lock(&global_mutex);
+								std::cerr << " pdf_offset= " << pdf_offset << " pdf_orientation= " << pdf_orientation << std::endl;
+								std::cerr << " ipart= " << ipart << " part_id= " << part_id << std::endl;
+								std::cerr << " iorient= " << iorient << " idir= " << idir << " ipsi= " << ipsi << std::endl;
+								//std::cerr << " exp_nr_psi= " << exp_nr_psi << " exp_nr_dir= " << exp_nr_dir << " exp_nr_trans= " << exp_nr_trans << std::endl;
+								for (long int i = 0; i < exp_directions_prior.size(); i++)
+									std::cerr << " exp_directions_prior["<<i<<"]= " << exp_directions_prior[i] << std::endl;
+								for (long int i = 0; i < exp_psi_prior.size(); i++)
+									std::cerr << " exp_psi_prior["<<i<<"]= " << exp_psi_prior[i] << std::endl;
+								REPORT_ERROR("ERROR! pdf_offset==0.|| pdf_orientation==0.");
+								pthread_mutex_unlock(&global_mutex);
+							}
+							if (exp_nr_oversampled_rot == 0)
+								REPORT_ERROR("exp_nr_oversampled_rot == 0");
+							if (exp_nr_oversampled_trans == 0)
+								REPORT_ERROR("exp_nr_oversampled_trans == 0");
 #ifdef TIMING
-				// Only time one thread, as I also only time one MPI process
-				if (thread_id == 0)
-					timer.toc(TIMING_WEIGHT_EXP);
+							// Only time one thread, as I also only time one MPI process
+							if (my_ori_particle == exp_my_first_ori_particle)
+								timer.tic(TIMING_WEIGHT_EXP);
 #endif
-			} // end loop itrans
-
-		} // end loop iorient
-	} // end while task distributor
-
-	// Now inside a mutex update the sum of all weights
-	global_mutex.lock();
-	exp_thisparticle_sumweight += thisthread_sumweight;
-	global_mutex.unlock();
-
-	// Wait until all threads have finished
-	global_barrier->wait();
-
-
-
-#ifdef DEBUG_THREAD
-    std::cerr << "leaving doThreadConvertSquaredDifferencesToWeightsAllOrientations" << std::endl;
+							// Now first loop over iover_rot, because that is the order in exp_Mweight as well
+							long int ihidden_over = ihidden * exp_nr_oversampled_rot * exp_nr_oversampled_trans;
+							for (long int iover_rot = 0; iover_rot < exp_nr_oversampled_rot; iover_rot++)
+							{
+								// Then loop over iover_trans
+								for (long int iover_trans = 0; iover_trans < exp_nr_oversampled_trans; iover_trans++, ihidden_over++)
+								{
+#ifdef DEBUG_CHECKSIZES
+									if (ihidden_over >= XSIZE(exp_Mweight))
+									{
+										std::cerr<< "ihidden_over= "<<ihidden_over<<" XSIZE(Mweight)= "<< XSIZE(exp_Mweight) <<std::endl;
+										REPORT_ERROR("ihidden_over >= XSIZE(exp_Mweight)");
+									}
 #endif
-}
-
-void MlOptimiser::convertAllSquaredDifferencesToWeights()
-{
-
-#ifdef TIMING
-	if (exp_ipass == 0) timer.tic(TIMING_ESP_WEIGHT1);
-	else timer.tic(TIMING_ESP_WEIGHT2);
+									// Only exponentiate for determined values of exp_Mweight
+									// (this is always true in the first pass, but not so in the second pass)
+									// Only deal with this sampling point if its weight was significant
+#ifdef DEBUG_CHECKSIZES
+									if (ipart >= YSIZE(exp_Mweight))
+									{
+										std::cerr << " YSIZE(exp_Mweight)= "<< YSIZE(exp_Mweight) <<std::endl;
+										std::cerr << " ipart= " << ipart << std::endl;
+										REPORT_ERROR("ipart >= YSIZE(exp_Mweight)");
+									}
 #endif
-
-	// Convert the squared differences into weights
-	// Note there is only one weight for each part_id, because a whole series of images is treated as one particle
-
-	// Initialising...
-	exp_sum_weight.resize(exp_nr_particles);
-	for (int i = 0; i < exp_nr_particles; i++)
-		exp_sum_weight[i] = 0.;
-
-//#define DEBUG_CONVERTDIFF2W
-#ifdef DEBUG_CONVERTDIFF2W
-	double max_weight = -1.;
-	double opt_psi, opt_xoff, opt_yoff;
-	int opt_iover_rot, opt_iover_trans, opt_ipsi, opt_itrans;
-	long int opt_ihidden, opt_ihidden_over;
+									if (DIRECT_A2D_ELEM(exp_Mweight, ipart, ihidden_over) < 0.)
+									{
+										DIRECT_A2D_ELEM(exp_Mweight, ipart, ihidden_over) = 0.;
+									}
+									else
+									{
+										DOUBLE weight = pdf_orientation * pdf_offset;
+										DOUBLE diff2 = DIRECT_A2D_ELEM(exp_Mweight, ipart, ihidden_over) - exp_min_diff2[ipart];
+										// next line because of numerical precision of exp-function
+										if (diff2 > 700.) weight = 0.;
+										// TODO: use tabulated exp function?
+										else weight *= exp(-diff2);
+//#define DEBUG_PSIANGLE_PDISTRIBUTION
+#ifdef DEBUG_PSIANGLE_PDISTRIBUTION
+										std::cout << ipsi*360./sampling.NrPsiSamplings() << " "<< weight << std::endl;
 #endif
+										// Store the weight
+										DIRECT_A2D_ELEM(exp_Mweight, ipart, ihidden_over) = weight;
+#ifdef DEBUG_CHECKSIZES
+										if (std::isnan(weight))
+										{
+											pthread_mutex_lock(&global_mutex);
+											std::cerr<< "weight= "<<weight<<" is not a number! " <<std::endl;
+											std::cerr << " exp_min_diff2[ipart]= " << exp_min_diff2[ipart] << std::endl;
+											std::cerr << " ipart= " << ipart << std::endl;
+											std::cerr << " part_id= " << part_id << std::endl;
+											std::cerr << " DIRECT_A2D_ELEM(exp_Mweight, ipart, ihidden_over)= " << DIRECT_A2D_ELEM(exp_Mweight, ipart, ihidden_over) << std::endl;
+											REPORT_ERROR("weight is not a number");
+											pthread_mutex_unlock(&global_mutex);
+										}
+#endif
+										// Keep track of sum and maximum of all weights for this particle
+										// Later add all to exp_thisparticle_sumweight, but inside this loop sum to local thisthread_sumweight first
+										exp_thisparticle_sumweight += weight;
+									} // end if/else exp_Mweight < 0.
+								} // end loop iover_trans
+							}// end loop iover_rot
+#ifdef TIMING
+							// Only time one thread, as I also only time one MPI process
+							if (my_ori_particle == exp_my_first_ori_particle)
+								timer.toc(TIMING_WEIGHT_EXP);
+#endif
+						} // end loop itrans
+					} // end loop ipsi
+				} // end loop idir
+			} // end loop exp_iclass
+		} // end if iter==1
 
-	//TMP DEBUGGING
-	//DEBUGGING_COPY_exp_Mweight = exp_Mweight;
+		//Store parameters for this particle
+		exp_sum_weight[ipart] = exp_thisparticle_sumweight;
 
-	// Loop from iclass_min to iclass_max to deal with seed generation in first iteration
-	exp_iimage = 0;
-	exp_ipart = 0;
-	for (long int ori_part_id = exp_my_first_ori_particle; ori_part_id <= exp_my_last_ori_particle; ori_part_id++)
-	{
-		// loop over all particles inside this ori_particle
-		for (long int i = 0; i < mydata.ori_particles[ori_part_id].particles_id.size(); i++, exp_ipart++)
+		// Check the sum of weights is not zero
+// On a Mac, the isnan function does not compile. Just uncomment the define statement, as this is merely a debugging statement
+//#define MAC_OSX
+#ifndef MAC_OSX
+		if (exp_thisparticle_sumweight == 0. || std::isnan(exp_thisparticle_sumweight))
 		{
-			exp_part_id = mydata.ori_particles[ori_part_id].particles_id[i];
-			exp_thisparticle_sumweight = 0.;
-
-
-			if ((iter == 1 && do_firstiter_cc) || do_always_cc)
+			std::cerr << " exp_thisparticle_sumweight= " << exp_thisparticle_sumweight << std::endl;
+			Image<DOUBLE> It;
+			It() = exp_Mweight;
+			It.write("Mweight.spi");
+			//It() = DEBUGGING_COPY_exp_Mweight;
+			//It.write("Mweight_copy.spi");
+			It().resize(exp_Mcoarse_significant);
+			if (MULTIDIM_SIZE(It()) > 0)
 			{
-
-				// Binarize the squared differences array to skip marginalisation
-				// Note this loop is not threaded. This is not so important because it will only be executed in the 1st iteration and is fast anyway
-				double mymindiff2 = 99.e10, mymaxprob = -99.e10;
-				long int myminidx = -1;
-				// Find the smallest element in this row of exp_Mweight
-				for (long int i = 0; i < XSIZE(exp_Mweight); i++)
+				FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(It())
 				{
-
-					double cc = DIRECT_A2D_ELEM(exp_Mweight, exp_ipart, i);
-					// ignore non-determined cc
-					if (cc == -999.)
-						continue;
-
-					if (do_sim_anneal && iter > 1)
-					{
-						// P_accept = exp ( - (CCold -CC)/temperature)
-						// cc is negative value, so use "+ cc"
-						double my_prob = rnd_unif() * exp(-(exp_local_oldcc[exp_ipart] + cc)/temperature);
-						if (my_prob > mymaxprob)
-						{
-							mymaxprob = my_prob;
-							mymindiff2 = cc;
-							myminidx = i;
-						}
-					}
+					if (DIRECT_MULTIDIM_ELEM(exp_Mcoarse_significant, n))
+						DIRECT_MULTIDIM_ELEM(It(), n) = 1.;
 					else
-					{
-						// just search for the maximum
-						if (cc < mymindiff2)
-						{
-							mymindiff2 = cc;
-							myminidx = i;
-						}
-					}
+						DIRECT_MULTIDIM_ELEM(It(), n) = 0.;
 				}
-				// Set all except for the best hidden variable to zero and the smallest element to 1
-				for (long int i = 0; i < XSIZE(exp_Mweight); i++)
-					DIRECT_A2D_ELEM(exp_Mweight, exp_ipart, i)= 0.;
-
-				DIRECT_A2D_ELEM(exp_Mweight, exp_ipart, myminidx)= 1.;
-				exp_thisparticle_sumweight += 1.;
-
+				It.write("Mcoarse_significant.spi");
 			}
-			else
-			{
-				for (exp_iclass = iclass_min; exp_iclass <= iclass_max; exp_iclass++)
-				{
-
-					// The loops over all orientations are parallelised using threads
-					exp_iorient_ThreadTaskDistributor->reset(); // reset thread distribution tasks
-					global_ThreadManager->run(globalThreadConvertSquaredDifferencesToWeightsAllOrientations);
-
-				} // end loop iclass
-
-			} // end else iter==1 && do_firstiter_cc
-
-			// Keep track of number of processed images
-			exp_iimage += mydata.getNrImagesInSeries(exp_part_id);
-
-			//Store parameters for this particle
-			exp_sum_weight[exp_ipart] = exp_thisparticle_sumweight;
-
-			// Check the sum of weights is not zero
-// On a Mac, the isnan function does not compile. Just uncomment the define statement, as this is merely a debugging statement
-//#define MAC_OSX
-#ifndef MAC_OSX
-			if (exp_thisparticle_sumweight == 0. || std::isnan(exp_thisparticle_sumweight))
+			std::cerr << " part_id= " << part_id << std::endl;
+			int group_id = mydata.getGroupId(part_id);
+			std::cerr << " group_id= " << group_id << " mymodel.scale_correction[group_id]= " << mymodel.scale_correction[group_id] << std::endl;
+			std::cerr << " exp_ipass= " << exp_ipass << std::endl;
+			std::cerr << " sampling.NrDirections(0, true)= " << sampling.NrDirections()
+					<< " sampling.NrDirections(0, false)= " << sampling.NrDirections(0, &exp_pointer_dir_nonzeroprior) << std::endl;
+			std::cerr << " sampling.NrPsiSamplings(0, true)= " << sampling.NrPsiSamplings()
+					<< " sampling.NrPsiSamplings(0, false)= " << sampling.NrPsiSamplings(0, &exp_pointer_psi_nonzeroprior) << std::endl;
+			std::cerr << " mymodel.sigma2_noise[ipart]= " << mymodel.sigma2_noise[ipart] << std::endl;
+			std::cerr << " wsum_model.sigma2_noise[ipart]= " << wsum_model.sigma2_noise[ipart] << std::endl;
+			if (mymodel.orientational_prior_mode == NOPRIOR)
+				std::cerr << " wsum_model.pdf_direction[ipart]= " << wsum_model.pdf_direction[ipart] << std::endl;
+			if (do_norm_correction)
 			{
-				std::cerr << " exp_thisparticle_sumweight= " << exp_thisparticle_sumweight << std::endl;
-				Image<double> It;
-				It() = exp_Mweight;
-				It.write("Mweight.spi");
-				//It() = DEBUGGING_COPY_exp_Mweight;
-				//It.write("Mweight_copy.spi");
-				It().resize(exp_Mcoarse_significant);
-				if (MULTIDIM_SIZE(It()) > 0)
-				{
-					FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(It())
-					{
-						if (DIRECT_MULTIDIM_ELEM(exp_Mcoarse_significant, n))
-							DIRECT_MULTIDIM_ELEM(It(), n) = 1.;
-						else
-							DIRECT_MULTIDIM_ELEM(It(), n) = 0.;
-					}
-					It.write("Mcoarse_significant.spi");
-				}
-				std::cerr << " exp_part_id= " << exp_part_id << "exp_iimage="<<exp_iimage<<std::endl;
-				int group_id = mydata.getGroupId(exp_part_id, 0);
-				std::cerr << " group_id= " << group_id << " mymodel.scale_correction[group_id]= " << mymodel.scale_correction[group_id] << std::endl;
-				std::cerr << " exp_ipass= " << exp_ipass << std::endl;
-				std::cerr << " sampling.NrDirections(0, true)= " << sampling.NrDirections(0, true)
-						<< " sampling.NrDirections(0, false)= " << sampling.NrDirections(0, false) << std::endl;
-				std::cerr << " sampling.NrPsiSamplings(0, true)= " << sampling.NrPsiSamplings(0, true)
-						<< " sampling.NrPsiSamplings(0, false)= " << sampling.NrPsiSamplings(0, false) << std::endl;
-				std::cerr << " mymodel.sigma2_noise[exp_ipart]= " << mymodel.sigma2_noise[exp_ipart] << std::endl;
-				std::cerr << " wsum_model.sigma2_noise[exp_ipart]= " << wsum_model.sigma2_noise[exp_ipart] << std::endl;
-				if (mymodel.orientational_prior_mode == NOPRIOR)
-					std::cerr << " wsum_model.pdf_direction[exp_ipart]= " << wsum_model.pdf_direction[exp_ipart] << std::endl;
-				if (do_norm_correction)
-				{
-					std::cerr << " mymodel.avg_norm_correction= " << mymodel.avg_norm_correction << std::endl;
-					std::cerr << " wsum_model.avg_norm_correction= " << wsum_model.avg_norm_correction << std::endl;
-				}
-
-				std::cerr << "written out Mweight.spi" << std::endl;
-				std::cerr << " exp_thisparticle_sumweight= " << exp_thisparticle_sumweight << std::endl;
-				std::cerr << " exp_min_diff2[exp_ipart]= " << exp_min_diff2[exp_ipart] << std::endl;
-				REPORT_ERROR("ERROR!!! zero sum of weights....");
+				std::cerr << " mymodel.avg_norm_correction= " << mymodel.avg_norm_correction << std::endl;
+				std::cerr << " wsum_model.avg_norm_correction= " << wsum_model.avg_norm_correction << std::endl;
 			}
-#endif
 
-		} // end loop part_id (i)
-	} // end loop ori_part_id
+			std::cerr << "written out Mweight.spi" << std::endl;
+			std::cerr << " exp_thisparticle_sumweight= " << exp_thisparticle_sumweight << std::endl;
+			std::cerr << " exp_min_diff2[ipart]= " << exp_min_diff2[ipart] << std::endl;
+			REPORT_ERROR("ERROR!!! zero sum of weights....");
+		}
+#endif
 
-	// The remainder of this function is not threaded.
+	} // end loop ipart
 
 	// Initialise exp_Mcoarse_significant
 	if (exp_ipass==0)
@@ -3995,285 +4113,328 @@ void MlOptimiser::convertAllSquaredDifferencesToWeights()
 	// Now, for each particle,  find the exp_significant_weight that encompasses adaptive_fraction of exp_sum_weight
 	exp_significant_weight.clear();
 	exp_significant_weight.resize(exp_nr_particles, 0.);
-	for (long int ori_part_id = exp_my_first_ori_particle, my_image_no = 0, ipart = 0; ori_part_id <= exp_my_last_ori_particle; ori_part_id++)
+	for (long int ipart = 0; ipart < exp_nr_particles; ipart++)
 	{
-		// loop over all particles inside this ori_particle
-		for (long int i = 0; i < mydata.ori_particles[ori_part_id].particles_id.size(); i++, ipart++)
-		{
-			long int part_id = mydata.ori_particles[ori_part_id].particles_id[i];
+		long int part_id = mydata.ori_particles[my_ori_particle].particles_id[ipart];
 
-	#ifdef TIMING
-	timer.tic(TIMING_WEIGHT_SORT);
-	#endif
-			MultidimArray<double> sorted_weight;
-			// Get the relevant row for this particle
-			exp_Mweight.getRow(ipart, sorted_weight);
+#ifdef TIMING
+		if (my_ori_particle == exp_my_first_ori_particle)
+			timer.tic(TIMING_WEIGHT_SORT);
+#endif
+		MultidimArray<DOUBLE> sorted_weight;
+		// Get the relevant row for this particle
+		exp_Mweight.getRow(ipart, sorted_weight);
 
-			// Only select non-zero probabilities to speed up sorting
-			long int np = 0;
-			FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(sorted_weight)
-			{
-				if (DIRECT_MULTIDIM_ELEM(sorted_weight, n) > 0.)
-				{
-					DIRECT_MULTIDIM_ELEM(sorted_weight, np) = DIRECT_MULTIDIM_ELEM(sorted_weight, n);
-					np++;
-				}
-			}
-			sorted_weight.resize(np);
-
-			// Sort from low to high values
-			sorted_weight.sort();
-
-	#ifdef TIMING
-	timer.toc(TIMING_WEIGHT_SORT);
-	#endif
-			double frac_weight = 0.;
-			double my_significant_weight;
-			long int my_nr_significant_coarse_samples = 0;
-			for (long int i = XSIZE(sorted_weight) - 1; i >= 0; i--)
+		// Only select non-zero probabilities to speed up sorting
+		long int np = 0;
+		FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(sorted_weight)
+		{
+			if (DIRECT_MULTIDIM_ELEM(sorted_weight, n) > 0.)
 			{
-				if (exp_ipass==0) my_nr_significant_coarse_samples++;
-				my_significant_weight = DIRECT_A1D_ELEM(sorted_weight, i);
-				frac_weight += my_significant_weight;
-				if (frac_weight > adaptive_fraction * exp_sum_weight[ipart])
-					break;
+				DIRECT_MULTIDIM_ELEM(sorted_weight, np) = DIRECT_MULTIDIM_ELEM(sorted_weight, n);
+				np++;
 			}
+		}
+		sorted_weight.resize(np);
 
-	#ifdef DEBUG_SORT
-			// Check sorted array is really sorted
-			double prev = 0.;
-			FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(sorted_weight)
-			{
-				if (DIRECT_MULTIDIM_ELEM(sorted_weight, n) < prev)
-				{
-					Image<double> It;
-					It()=sorted_weight;
-					It() *= 10000;
-					It.write("sorted_weight.spi");
-					std::cerr << "written sorted_weight.spi" << std::endl;
-					REPORT_ERROR("Error in sorting!");
-				}
-				prev=DIRECT_MULTIDIM_ELEM(sorted_weight, n);
-			}
-	#endif
+		// Sort from low to high values
+		sorted_weight.sort();
+
+#ifdef TIMING
+		if (my_ori_particle == exp_my_first_ori_particle)
+			timer.toc(TIMING_WEIGHT_SORT);
+#endif
+		DOUBLE frac_weight = 0.;
+		DOUBLE my_significant_weight;
+		long int my_nr_significant_coarse_samples = 0;
+		for (long int i = XSIZE(sorted_weight) - 1; i >= 0; i--)
+		{
+			if (exp_ipass==0) my_nr_significant_coarse_samples++;
+			my_significant_weight = DIRECT_A1D_ELEM(sorted_weight, i);
+			frac_weight += my_significant_weight;
+			if (frac_weight > adaptive_fraction * exp_sum_weight[ipart])
+				break;
+		}
 
-			if (exp_ipass==0 && my_nr_significant_coarse_samples == 0)
+#ifdef DEBUG_SORT
+		// Check sorted array is really sorted
+		DOUBLE prev = 0.;
+		FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(sorted_weight)
+		{
+			if (DIRECT_MULTIDIM_ELEM(sorted_weight, n) < prev)
 			{
-				std::cerr << " ipart= " << ipart << " adaptive_fraction= " << adaptive_fraction << std::endl;
-				std::cerr << " frac-weight= " << frac_weight << std::endl;
-				std::cerr << " exp_sum_weight[ipart]= " << exp_sum_weight[ipart] << std::endl;
-				Image<double> It;
-				std::cerr << " XSIZE(exp_Mweight)= " << XSIZE(exp_Mweight) << std::endl;
-				It()=exp_Mweight;
-				It() *= 10000;
-				It.write("Mweight2.spi");
-				std::cerr << "written Mweight2.spi" << std::endl;
-				std::cerr << " np= " << np << std::endl;
+				Image<DOUBLE> It;
 				It()=sorted_weight;
 				It() *= 10000;
-				std::cerr << " XSIZE(sorted_weight)= " << XSIZE(sorted_weight) << std::endl;
-				if (XSIZE(sorted_weight) > 0)
-				{
-					It.write("sorted_weight.spi");
-					std::cerr << "written sorted_weight.spi" << std::endl;
-				}
-				REPORT_ERROR("my_nr_significant_coarse_samples == 0");
+				It.write("sorted_weight.spi");
+				std::cerr << "written sorted_weight.spi" << std::endl;
+				REPORT_ERROR("Error in sorting!");
 			}
+			prev=DIRECT_MULTIDIM_ELEM(sorted_weight, n);
+		}
+#endif
 
-			if (exp_ipass==0)
+		if (exp_ipass==0 && my_nr_significant_coarse_samples == 0)
+		{
+			std::cerr << " ipart= " << ipart << " adaptive_fraction= " << adaptive_fraction << std::endl;
+			std::cerr << " frac-weight= " << frac_weight << std::endl;
+			std::cerr << " exp_sum_weight[ipart]= " << exp_sum_weight[ipart] << std::endl;
+			Image<DOUBLE> It;
+			std::cerr << " XSIZE(exp_Mweight)= " << XSIZE(exp_Mweight) << std::endl;
+			It()=exp_Mweight;
+			It() *= 10000;
+			It.write("Mweight2.spi");
+			std::cerr << "written Mweight2.spi" << std::endl;
+			std::cerr << " np= " << np << std::endl;
+			It()=sorted_weight;
+			It() *= 10000;
+			std::cerr << " XSIZE(sorted_weight)= " << XSIZE(sorted_weight) << std::endl;
+			if (XSIZE(sorted_weight) > 0)
 			{
-				// Store nr_significant_coarse_samples for all images in this series
-				for (int iseries = 0; iseries < mydata.getNrImagesInSeries(part_id); iseries++, my_image_no++)
-				{
-					DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NR_SIGN) = (double)my_nr_significant_coarse_samples;
-				}
+				It.write("sorted_weight.spi");
+				std::cerr << "written sorted_weight.spi" << std::endl;
+			}
+			REPORT_ERROR("my_nr_significant_coarse_samples == 0");
+		}
 
-				// Keep track of which coarse samplings were significant were significant for this particle
-				for (int ihidden = 0; ihidden < XSIZE(exp_Mcoarse_significant); ihidden++)
-				{
-					if (DIRECT_A2D_ELEM(exp_Mweight, ipart, ihidden) >= my_significant_weight)
-						DIRECT_A2D_ELEM(exp_Mcoarse_significant, ipart, ihidden) = true;
-					else
-						DIRECT_A2D_ELEM(exp_Mcoarse_significant, ipart, ihidden) = false;
-				}
+		if (exp_ipass==0)
+		{
+			// Store nr_significant_coarse_samples for this particle
+			DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_NR_SIGN) = (DOUBLE)my_nr_significant_coarse_samples;
 
+			// Keep track of which coarse samplings were significant were significant for this particle
+			for (int ihidden = 0; ihidden < XSIZE(exp_Mcoarse_significant); ihidden++)
+			{
+				if (DIRECT_A2D_ELEM(exp_Mweight, ipart, ihidden) >= my_significant_weight)
+					DIRECT_A2D_ELEM(exp_Mcoarse_significant, ipart, ihidden) = true;
+				else
+					DIRECT_A2D_ELEM(exp_Mcoarse_significant, ipart, ihidden) = false;
 			}
-			exp_significant_weight[ipart] = my_significant_weight;
-	#ifdef DEBUG_OVERSAMPLING
-			std::cerr << " sum_weight[ipart]= " << exp_sum_weight[ipart] << " my_significant_weight= " << my_significant_weight << std::endl;
-			std::cerr << " my_nr_significant_coarse_samples= " << my_nr_significant_coarse_samples << std::endl;
-			std::cerr << " ipass= " << exp_ipass << " Pmax="<<DIRECT_A1D_ELEM(sorted_weight,XSIZE(sorted_weight) - 1)/frac_weight
-					<<" nr_sign_sam= "<<nr_significant_samples<<" sign w= "<<exp_significant_weight<< "sum_weight= "<<exp_sum_weight<<std::endl;
-	#endif
 
-		} // end loop part_id (i)
-	} // end loop ori_part_id
+		}
+		exp_significant_weight[ipart] = my_significant_weight;
+#ifdef DEBUG_OVERSAMPLING
+		std::cerr << " sum_weight[ipart]= " << exp_sum_weight[ipart] << " my_significant_weight= " << my_significant_weight << std::endl;
+		std::cerr << " my_nr_significant_coarse_samples= " << my_nr_significant_coarse_samples << std::endl;
+		std::cerr << " ipass= " << exp_ipass << " Pmax="<<DIRECT_A1D_ELEM(sorted_weight,XSIZE(sorted_weight) - 1)/frac_weight
+				<<" nr_sign_sam= "<<nr_significant_samples<<" sign w= "<<exp_significant_weight<< "sum_weight= "<<exp_sum_weight<<std::endl;
+#endif
+
+	} // end loop ipart
 
 
 #ifdef DEBUG_CONVERTDIFF2W
-	//Image<double> tt;
+	//Image<DOUBLE> tt;
 	//tt()=sorted_weight;
 	//tt.write("sorted_weight.spi");
 	//std::cerr << "written sorted_weight.spi" << std::endl;
-	std::cerr << " ipass= " << exp_ipass << " exp_part_id= " << exp_part_id << std::endl;
+	std::cerr << " ipass= " << exp_ipass << " part_id= " << part_id << std::endl;
 	std::cerr << " diff2w: opt_xoff= " << opt_xoff << " opt_yoff= " << opt_yoff << " opt_psi= " << opt_psi << std::endl;
 	std::cerr << " diff2w: opt_iover_rot= " << opt_iover_rot << " opt_iover_trans= " << opt_iover_trans << " opt_ipsi= " << opt_ipsi << std::endl;
 	std::cerr << " diff2w: opt_itrans= " << opt_itrans << " opt_ihidden= " << opt_ihidden << " opt_ihidden_over= " << opt_ihidden_over << std::endl;
 	std::cerr << "significant_weight= " << exp_significant_weight << " max_weight= " << max_weight << std::endl;
 	std::cerr << "nr_significant_coarse_samples= " << nr_significant_coarse_samples <<std::endl;
-	debug2 = (double)opt_ihidden_over;
+	debug2 = (DOUBLE)opt_ihidden_over;
 #endif
 
 #ifdef TIMING
-	if (exp_ipass == 0) timer.toc(TIMING_ESP_WEIGHT1);
-	else timer.toc(TIMING_ESP_WEIGHT2);
+	if (my_ori_particle == exp_my_first_ori_particle)
+	{
+		if (exp_ipass == 0) timer.toc(TIMING_ESP_WEIGHT1);
+		else timer.toc(TIMING_ESP_WEIGHT2);
+	}
 #endif
 
 }
 
-void MlOptimiser::doThreadStoreWeightedSumsAllOrientations(int thread_id)
+void MlOptimiser::storeWeightedSums(long int my_ori_particle, int exp_current_image_size,
+		int exp_current_oversampling, int metadata_offset,
+		int exp_idir_min, int exp_idir_max, int exp_ipsi_min, int exp_ipsi_max,
+		int exp_itrans_min, int exp_itrans_max, int exp_iclass_min, int exp_iclass_max,
+		std::vector<DOUBLE> &exp_min_diff2,
+		std::vector<DOUBLE> &exp_highres_Xi2_imgs,
+		std::vector<MultidimArray<Complex > > &exp_Fimgs,
+		std::vector<MultidimArray<Complex > > &exp_Fimgs_nomask,
+		std::vector<MultidimArray<DOUBLE> > &exp_Fctfs,
+		std::vector<MultidimArray<DOUBLE> > &exp_power_imgs,
+		std::vector<Matrix1D<DOUBLE> > &exp_old_offset,
+		std::vector<Matrix1D<DOUBLE> > &exp_prior,
+		MultidimArray<DOUBLE> &exp_Mweight,
+		MultidimArray<bool> &exp_Mcoarse_significant,
+		std::vector<DOUBLE> &exp_significant_weight,
+		std::vector<DOUBLE> &exp_sum_weight,
+		std::vector<DOUBLE> &exp_max_weight,
+		std::vector<int> &exp_pointer_dir_nonzeroprior, std::vector<int> &exp_pointer_psi_nonzeroprior,
+		std::vector<DOUBLE> &exp_directions_prior, std::vector<DOUBLE> &exp_psi_prior,
+		std::vector<MultidimArray<Complex > > &exp_local_Fimgs_shifted,
+		std::vector<MultidimArray<Complex > > &exp_local_Fimgs_shifted_nomask,
+		std::vector<MultidimArray<DOUBLE> > &exp_local_Minvsigma2s,
+		std::vector<MultidimArray<DOUBLE> > &exp_local_Fctfs,
+		std::vector<DOUBLE> &exp_local_sqrtXi2)
 {
-#ifdef DEBUG_THREAD
-    std::cerr << "entering doThreadStoreWeightedSumsAllOrientations" << std::endl;
+
+#ifdef TIMING
+	if (my_ori_particle == exp_my_first_ori_particle)
+		timer.tic(TIMING_ESP_WSUM);
 #endif
 
-	std::vector< Matrix1D<double> > oversampled_orientations, oversampled_translations;
-	Matrix2D<double> A;
-	MultidimArray<Complex > Fimg, Fref, Frefctf, Fimg_shift, Fimg_shift_nomask;
-	MultidimArray<double> Minvsigma2, Mctf, Fweight;
-	double rot, tilt, psi;
-	bool have_warned_small_scale = false;
+	int exp_nr_particles = mydata.ori_particles[my_ori_particle].particles_id.size();
+	long int exp_nr_dir = (do_skip_align || do_skip_rotate) ? 1 : sampling.NrDirections(0, &exp_pointer_dir_nonzeroprior);
+	long int exp_nr_psi = (do_skip_align || do_skip_rotate) ? 1 : sampling.NrPsiSamplings(0, &exp_pointer_psi_nonzeroprior);
+	long int exp_nr_trans = (do_skip_align) ? 1 : sampling.NrTranslationalSamplings();
+	long int exp_nr_oversampled_rot = sampling.oversamplingFactorOrientations(exp_current_oversampling);
+	long int exp_nr_oversampled_trans = sampling.oversamplingFactorTranslations(exp_current_oversampling);
 
-	// Initialising...
+	// Re-do below because now also want unmasked images AND if (stricht_highres_exp >0.) then may need to resize
+	precalculateShiftedImagesCtfsAndInvSigma2s(true, my_ori_particle, exp_current_image_size, exp_current_oversampling,
+			exp_itrans_min, exp_itrans_max, exp_Fimgs, exp_Fimgs_nomask, exp_Fctfs, exp_local_Fimgs_shifted, exp_local_Fimgs_shifted_nomask,
+			exp_local_Fctfs, exp_local_sqrtXi2, exp_local_Minvsigma2s);
+
+	// In doThreadPrecalculateShiftedImagesCtfsAndInvSigma2s() the origin of the exp_local_Minvsigma2s was omitted.
+	// Set those back here
+	for (long int ipart = 0; ipart < mydata.ori_particles[my_ori_particle].particles_id.size(); ipart++)
+	{
+		long int part_id = mydata.ori_particles[my_ori_particle].particles_id[ipart];
+		int group_id = mydata.getGroupId(part_id);
+		DIRECT_MULTIDIM_ELEM(exp_local_Minvsigma2s[ipart], 0) = 1. / (sigma2_fudge * DIRECT_A1D_ELEM(mymodel.sigma2_noise[group_id], 0));
+	}
+
+	// Initialise the maximum of all weights to a negative value
+	exp_max_weight.clear();
+	exp_max_weight.resize(exp_nr_particles, -1.);
+
+	// For norm_correction and scale_correction of all particles of this ori_particle
+	std::vector<DOUBLE> exp_wsum_norm_correction;
+	std::vector<MultidimArray<DOUBLE> > exp_wsum_scale_correction_XA, exp_wsum_scale_correction_AA;
+	std::vector<MultidimArray<DOUBLE> > thr_wsum_signal_product_spectra, thr_wsum_reference_power_spectra;
+	exp_wsum_norm_correction.resize(exp_nr_particles, 0.);
+
+	// For scale_correction
+	if (do_scale_correction)
+	{
+		MultidimArray<DOUBLE> aux;
+		aux.initZeros(mymodel.ori_size/2 + 1);
+		exp_wsum_scale_correction_XA.resize(exp_nr_particles, aux);
+		exp_wsum_scale_correction_AA.resize(exp_nr_particles, aux);
+		thr_wsum_signal_product_spectra.resize(mymodel.nr_groups, aux);
+		thr_wsum_reference_power_spectra.resize(mymodel.nr_groups, aux);
+	}
+
+	std::vector< DOUBLE> oversampled_rot, oversampled_tilt, oversampled_psi;
+	std::vector<DOUBLE> oversampled_translations_x, oversampled_translations_y, oversampled_translations_z;
+	Matrix2D<DOUBLE> A;
+	MultidimArray<Complex > Fimg, Fref, Frefctf, Fimg_otfshift, Fimg_otfshift_nomask;
+	MultidimArray<DOUBLE> Minvsigma2, Mctf, Fweight;
+	DOUBLE rot, tilt, psi;
+	bool have_warned_small_scale = false;
+	// Initialising... exp_Fimgs[0] has mymodel.current_size (not coarse_size!)
 	Fref.resize(exp_Fimgs[0]);
 	Frefctf.resize(exp_Fimgs[0]);
 	Fweight.resize(exp_Fimgs[0]);
-
+	Fimg.resize(exp_Fimgs[0]);
 	// Initialise Mctf to all-1 for if !do_ctf_corection
 	Mctf.resize(exp_Fimgs[0]);
 	Mctf.initConstant(1.);
-
 	// Initialise Minvsigma2 to all-1 for if !do_map
 	Minvsigma2.resize(exp_Fimgs[0]);
 	Minvsigma2.initConstant(1.);
+	if (do_shifts_onthefly)
+	{
+		Fimg_otfshift.resize(Frefctf);
+		Fimg_otfshift_nomask.resize(Frefctf);
+	}
 
-	// Make local copies of weighted sums (excepts BPrefs, which are too big)
+	// Make local copies of weighted sums (except BPrefs, which are too big)
 	// so that there are not too many mutex locks below
-	std::vector<MultidimArray<double> > thr_wsum_sigma2_noise, thr_wsum_scale_correction_XA, thr_wsum_scale_correction_AA, thr_wsum_pdf_direction;
-	std::vector<double> thr_wsum_norm_correction, thr_sumw_group, thr_wsum_pdf_class, thr_wsum_prior_offsetx_class, thr_wsum_prior_offsety_class, thr_max_weight;
-	double thr_wsum_sigma2_offset;
-	MultidimArray<double> thr_metadata, zeroArray;
-
+	std::vector<MultidimArray<DOUBLE> > thr_wsum_sigma2_noise, thr_wsum_pdf_direction;
+	std::vector<DOUBLE> thr_wsum_norm_correction, thr_sumw_group, thr_wsum_pdf_class, thr_wsum_prior_offsetx_class, thr_wsum_prior_offsety_class;
+	DOUBLE thr_wsum_sigma2_offset;
+	MultidimArray<DOUBLE> thr_metadata, zeroArray;
 	// Wsum_sigma_noise2 is a 1D-spectrum for each group
 	zeroArray.initZeros(mymodel.ori_size/2 + 1);
-	thr_wsum_sigma2_noise.resize(mymodel.nr_groups);
-	for (int n = 0; n < mymodel.nr_groups; n++)
-	{
-		thr_wsum_sigma2_noise[n] = zeroArray;
-	}
-	// scale-correction terms are a spectrum for each particle
-	thr_wsum_scale_correction_XA.resize(exp_nr_particles);
-	thr_wsum_scale_correction_AA.resize(exp_nr_particles);
-	for (int n = 0; n < exp_nr_particles; n++)
-	{
-		thr_wsum_scale_correction_XA[n] = zeroArray;
-		thr_wsum_scale_correction_AA[n] = zeroArray;
-	}
-	// wsum_pdf_direction is a 1D-array (of length sampling.NrDirections(0, true)) for each class
-	zeroArray.initZeros(sampling.NrDirections(0, true));
-	thr_wsum_pdf_direction.resize(mymodel.nr_classes);
-	for (int n = 0; n < mymodel.nr_classes; n++)
-	{
-		thr_wsum_pdf_direction[n] = zeroArray;
-	}
-	// wsum_norm_correction is a double for each particle
-	thr_wsum_norm_correction.resize(exp_nr_particles, 0.);
-	// sumw_group is a double for each group
+	thr_wsum_sigma2_noise.resize(mymodel.nr_groups, zeroArray);
+	// wsum_pdf_direction is a 1D-array (of length sampling.NrDirections()) for each class
+	zeroArray.initZeros(sampling.NrDirections());
+	thr_wsum_pdf_direction.resize(mymodel.nr_classes, zeroArray);
+	// sumw_group is a DOUBLE for each group
 	thr_sumw_group.resize(mymodel.nr_groups, 0.);
-	// wsum_pdf_class is a double for each class
+	// wsum_pdf_class is a DOUBLE for each class
 	thr_wsum_pdf_class.resize(mymodel.nr_classes, 0.);
 	if (mymodel.ref_dim == 2)
 	{
 		thr_wsum_prior_offsetx_class.resize(mymodel.nr_classes, 0.);
 		thr_wsum_prior_offsety_class.resize(mymodel.nr_classes, 0.);
 	}
-	// max_weight is a double for each particle
-	thr_max_weight.resize(exp_nr_particles, 0.);
-	// wsum_sigma2_offset is just a double
+	// wsum_sigma2_offset is just a DOUBLE
 	thr_wsum_sigma2_offset = 0.;
-	// metadata is a 2D array of nr_particles x METADATA_LINE_LENGTH
-	thr_metadata.initZeros(exp_metadata);
 
-	// exp_iclass loop does not always go from 0 to nr_classes!
-	long int iorientclass_offset = exp_iclass * exp_nr_rot;
-	size_t first_iorient = 0, last_iorient = 0;
-	while (exp_iorient_ThreadTaskDistributor->getTasks(first_iorient, last_iorient))
+	// Loop from iclass_min to iclass_max to deal with seed generation in first iteration
+	for (int exp_iclass = exp_iclass_min; exp_iclass <= exp_iclass_max; exp_iclass++)
 	{
-		for (long int iorient = first_iorient; iorient <= last_iorient; iorient++)
+		for (long int idir = exp_idir_min, iorient = 0; idir <= exp_idir_max; idir++)
 		{
-
-			long int iorientclass = iorientclass_offset + iorient;
-
-			// Only proceed if any of the particles had any significant coarsely sampled translation
-			if (isSignificantAnyParticleAnyTranslation(iorientclass))
+			for (long int ipsi = exp_ipsi_min; ipsi <= exp_ipsi_max; ipsi++, iorient++)
 			{
+				long int iorientclass = exp_iclass * exp_nr_dir * exp_nr_psi + iorient;
 
-				long int idir = iorient / exp_nr_psi;
-				long int ipsi = iorient % exp_nr_psi;
-
-				// Now get the oversampled (rot, tilt, psi) triplets
-				// This will be only the original (rot,tilt,psi) triplet if (adaptive_oversampling==0)
-				sampling.getOrientations(idir, ipsi, adaptive_oversampling, oversampled_orientations);
-
-				// Loop over all oversampled orientations (only a single one in the first pass)
-				for (long int iover_rot = 0; iover_rot < exp_nr_oversampled_rot; iover_rot++)
+				// Only proceed if any of the particles had any significant coarsely sampled translation
+				if (isSignificantAnyParticleAnyTranslation(iorientclass, exp_itrans_min, exp_itrans_max, exp_Mcoarse_significant))
 				{
-					rot = XX(oversampled_orientations[iover_rot]);
-					tilt = YY(oversampled_orientations[iover_rot]);
-					psi = ZZ(oversampled_orientations[iover_rot]);
-					// Get the Euler matrix
-					Euler_angles2matrix(rot, tilt, psi, A);
-
-					// Take tilt-series into account
-					A = (exp_R_mic * A).inv();
-
+					// Now get the oversampled (rot, tilt, psi) triplets
+					// This will be only the original (rot,tilt,psi) triplet if (adaptive_oversampling==0)
+					sampling.getOrientations(idir, ipsi, adaptive_oversampling, oversampled_rot, oversampled_tilt, oversampled_psi,
+							exp_pointer_dir_nonzeroprior, exp_directions_prior, exp_pointer_psi_nonzeroprior, exp_psi_prior);
+					// Loop over all oversampled orientations (only a single one in the first pass)
+					for (long int iover_rot = 0; iover_rot < exp_nr_oversampled_rot; iover_rot++)
+					{
+						rot = oversampled_rot[iover_rot];
+						tilt = oversampled_tilt[iover_rot];
+						psi = oversampled_psi[iover_rot];
+						// Get the Euler matrix
+						Euler_angles2matrix(rot, tilt, psi, A);
 #ifdef TIMING
-					// Only time one thread, as I also only time one MPI process
-					if (thread_id == 0)
-						timer.tic(TIMING_WSUM_PROJ);
+						// Only time one thread, as I also only time one MPI process
+						if (my_ori_particle == exp_my_first_ori_particle)
+							timer.tic(TIMING_WSUM_PROJ);
 #endif
-					// Project the reference map (into Fref)
-					if (!do_skip_maximization)
-						(mymodel.PPref[exp_iclass]).get2DFourierTransform(Fref, A, IS_INV);
-
+						// Project the reference map (into Fref)
+						if (!do_skip_maximization)
+							(mymodel.PPref[exp_iclass]).get2DFourierTransform(Fref, A, IS_NOT_INV);
 #ifdef TIMING
-					// Only time one thread, as I also only time one MPI process
-					if (thread_id == 0)
-						timer.toc(TIMING_WSUM_PROJ);
-#endif
-					// Inside the loop over all translations and all part_id sum all shift Fimg's and their weights
-					// Then outside this loop do the actual backprojection
-					Fimg.initZeros(Fref);
-					Fweight.initZeros(Fref);
-
-					/// Now that reference projection has been made loop over someParticles!
-					for (long int ori_part_id = exp_my_first_ori_particle, ipart = 0; ori_part_id <= exp_my_last_ori_particle; ori_part_id++)
-					{
-						// loop over all particles inside this ori_particle
-						for (long int i = 0; i < mydata.ori_particles[ori_part_id].particles_id.size(); i++, ipart++)
+						// Only time one thread, as I also only time one MPI process
+						if (my_ori_particle == exp_my_first_ori_particle)
+							timer.toc(TIMING_WSUM_PROJ);
+#endif
+						// Inside the loop over all translations and all part_id sum all shift Fimg's and their weights
+						// Then outside this loop do the actual backprojection
+						Fimg.initZeros();
+						Fweight.initZeros();
+						/// Now that reference projection has been made loop over all particles inside this ori_particle
+						for (long int ipart = 0; ipart < mydata.ori_particles[my_ori_particle].particles_id.size(); ipart++)
 						{
-							long int part_id = mydata.ori_particles[ori_part_id].particles_id[i];
-#ifdef DEBUG_CHECKSIZES
-							if (ipart >= exp_starting_image_no.size())
+							// This is an attempt to speed up illogically slow updates of wsum_sigma2_offset....
+							// It seems to make a big difference!
+							DOUBLE myprior_x, myprior_y, myprior_z, old_offset_z;
+							DOUBLE old_offset_x = XX(exp_old_offset[ipart]);
+							DOUBLE old_offset_y = YY(exp_old_offset[ipart]);
+							if (mymodel.ref_dim == 2)
 							{
-								std::cerr<< "ipart= "<<ipart<<" starting_image_no.size()= "<< exp_starting_image_no.size() <<std::endl;
-								REPORT_ERROR("ipart >= starting_image_no.size()");
+								myprior_x = XX(mymodel.prior_offset_class[exp_iclass]);
+								myprior_y = YY(mymodel.prior_offset_class[exp_iclass]);
+							}
+							else
+							{
+								myprior_x = XX(exp_prior[ipart]);
+								myprior_y = YY(exp_prior[ipart]);
+								if (mymodel.data_dim == 3)
+								{
+									myprior_z = ZZ(exp_prior[ipart]);
+									old_offset_z = ZZ(exp_old_offset[ipart]);
+								}
 							}
-#endif
-							// Which number was this image in the combined array of iseries and part_idpart_id
-							long int my_image_no = exp_starting_image_no[ipart] + exp_iseries;
-							int group_id = mydata.getGroupId(part_id, exp_iseries);
 
+							long int part_id = mydata.ori_particles[my_ori_particle].particles_id[ipart];
+							int group_id = mydata.getGroupId(part_id);
 #ifdef DEBUG_CHECKSIZES
 							if (group_id >= mymodel.nr_groups)
 							{
@@ -4281,17 +4442,15 @@ void MlOptimiser::doThreadStoreWeightedSumsAllOrientations(int thread_id)
 								REPORT_ERROR("group_id >= ml_model.nr_groups");
 							}
 #endif
-
 							if (!do_skip_maximization)
 							{
 								if (do_map)
-									Minvsigma2 = exp_local_Minvsigma2s[my_image_no];
+									Minvsigma2 = exp_local_Minvsigma2s[ipart];
 								// else Minvsigma2 was initialised to ones
-
 								// Apply CTF to reference projection
 								if (do_ctf_correction)
 								{
-									Mctf = exp_local_Fctfs[my_image_no];
+									Mctf = exp_local_Fctfs[ipart];
 									if (refs_are_ctf_corrected)
 									{
 										FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Fref)
@@ -4310,19 +4469,16 @@ void MlOptimiser::doThreadStoreWeightedSumsAllOrientations(int thread_id)
 									Mctf.initConstant(1.);
 									Frefctf = Fref;
 								}
-
 								if (do_scale_correction)
 								{
-									// TODO: implemenent B-factor as well...
-									double myscale = mymodel.scale_correction[group_id];
+									DOUBLE myscale = mymodel.scale_correction[group_id];
 									if (myscale > 10000.)
 									{
-										std::cerr << " rlnMicrographScaleCorrection= " << myscale << " group= " << group_id + 1 << " my_image_no= " << my_image_no << std::endl;
+										std::cerr << " rlnMicrographScaleCorrection= " << myscale << " group= " << group_id + 1 << std::endl;
 										REPORT_ERROR("ERROR: rlnMicrographScaleCorrection is very high. Did you normalize your data?");
 									}
 									else if (myscale < 0.001)
 									{
-
 										if (!have_warned_small_scale)
 										{
 											std::cout << " WARNING: ignoring group " << group_id + 1 << " with very small or negative scale (" << myscale <<
@@ -4341,26 +4497,22 @@ void MlOptimiser::doThreadStoreWeightedSumsAllOrientations(int thread_id)
 							} // end if !do_skip_maximization
 
 							long int ihidden = iorientclass * exp_nr_trans;
-							for (long int itrans = 0; itrans < exp_nr_trans; itrans++, ihidden++)
+							for (long int itrans = exp_itrans_min, iitrans = 0; itrans <= exp_itrans_max; itrans++, ihidden++)
 							{
-
-								sampling.getTranslations(itrans, adaptive_oversampling, oversampled_translations);
-
-								for (long int iover_trans = 0; iover_trans < exp_nr_oversampled_trans; iover_trans++)
+								sampling.getTranslations(itrans, adaptive_oversampling,
+										oversampled_translations_x, oversampled_translations_y, oversampled_translations_z);
+								for (long int iover_trans = 0; iover_trans < exp_nr_oversampled_trans; iover_trans++, iitrans++)
 								{
-
 #ifdef DEBUG_CHECKSIZES
-									if (iover_trans >= oversampled_translations.size())
+									if (iover_trans >= oversampled_translations_x.size())
 									{
-										std::cerr<< "iover_trans= "<<iover_trans<<" oversampled_translations.size()= "<< oversampled_translations.size() <<std::endl;
-										REPORT_ERROR("iover_trans >= oversampled_translations.size()");
+										std::cerr<< "iover_trans= "<<iover_trans<<" oversampled_translations_x.size()= "<< oversampled_translations_x.size() <<std::endl;
+										REPORT_ERROR("iover_trans >= oversampled_translations_x.size()");
 									}
 #endif
-
 									// Only deal with this sampling point if its weight was significant
 									long int ihidden_over = ihidden * exp_nr_oversampled_trans * exp_nr_oversampled_rot +
 											iover_rot * exp_nr_oversampled_trans + iover_trans;
-
 #ifdef DEBUG_CHECKSIZES
 									if (ihidden_over >= XSIZE(exp_Mweight))
 									{
@@ -4383,522 +4535,390 @@ void MlOptimiser::doThreadStoreWeightedSumsAllOrientations(int thread_id)
 										REPORT_ERROR("ipart >= exp_sum_weight.size()");
 									}
 #endif
-									double weight = DIRECT_A2D_ELEM(exp_Mweight, ipart, ihidden_over);
-
+									DOUBLE weight = DIRECT_A2D_ELEM(exp_Mweight, ipart, ihidden_over);
 									// Only sum weights for non-zero weights
 									if (weight >= exp_significant_weight[ipart])
 									{
-#ifdef TIMING
-										// Only time one thread, as I also only time one MPI process
-										if (thread_id == 0)
-											timer.tic(TIMING_WSUM_DIFF2);
-#endif
 										// Normalise the weight (do this after the comparison with exp_significant_weight!)
 										weight /= exp_sum_weight[ipart];
-
 										if (!do_skip_maximization)
 										{
-											// Get the shifted image
-											long int ishift = my_image_no * exp_nr_oversampled_trans * exp_nr_trans +
-													itrans * exp_nr_oversampled_trans + iover_trans;
-											Fimg_shift = exp_local_Fimgs_shifted[ishift];
-											Fimg_shift_nomask = exp_local_Fimgs_shifted_nomask[ishift];
+
+#ifdef TIMING
+											// Only time one thread, as I also only time one MPI process
+											if (my_ori_particle == exp_my_first_ori_particle)
+												timer.tic(TIMING_WSUM_GETSHIFT);
+#endif
+
+											/// Now get the shifted image
+											// Use a pointer to avoid copying the entire array again in this highly expensive loop
+											Complex *Fimg_shift, *Fimg_shift_nomask;
+											if (!do_shifts_onthefly)
+											{
+												long int ishift = ipart * exp_nr_oversampled_trans * exp_nr_trans + iitrans;
+												Fimg_shift = exp_local_Fimgs_shifted[ishift].data;
+												Fimg_shift_nomask = exp_local_Fimgs_shifted_nomask[ishift].data;
+											}
+											else
+											{
+												Complex* myAB;
+												myAB = (adaptive_oversampling == 0 ) ? global_fftshifts_ab_current[iitrans].data : global_fftshifts_ab2_current[iitrans].data;
+												FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(exp_local_Fimgs_shifted[ipart])
+												{
+													DOUBLE a = (*(myAB + n)).real;
+													DOUBLE b = (*(myAB + n)).imag;
+													// Fimg_shift
+													DOUBLE real = a * (DIRECT_MULTIDIM_ELEM(exp_local_Fimgs_shifted[ipart], n)).real
+															- b *(DIRECT_MULTIDIM_ELEM(exp_local_Fimgs_shifted[ipart], n)).imag;
+													DOUBLE imag = a * (DIRECT_MULTIDIM_ELEM(exp_local_Fimgs_shifted[ipart], n)).imag
+															+ b *(DIRECT_MULTIDIM_ELEM(exp_local_Fimgs_shifted[ipart], n)).real;
+													DIRECT_MULTIDIM_ELEM(Fimg_otfshift, n) = Complex(real, imag);
+													// Fimg_shift_nomask
+													real = a * (DIRECT_MULTIDIM_ELEM(exp_local_Fimgs_shifted_nomask[ipart], n)).real
+															- b *(DIRECT_MULTIDIM_ELEM(exp_local_Fimgs_shifted_nomask[ipart], n)).imag;
+													imag = a * (DIRECT_MULTIDIM_ELEM(exp_local_Fimgs_shifted_nomask[ipart], n)).imag
+															+ b *(DIRECT_MULTIDIM_ELEM(exp_local_Fimgs_shifted_nomask[ipart], n)).real;
+													DIRECT_MULTIDIM_ELEM(Fimg_otfshift_nomask, n) = Complex(real, imag);
+												}
+												Fimg_shift = Fimg_otfshift.data;
+												Fimg_shift_nomask = Fimg_otfshift_nomask.data;
+											}
+#ifdef TIMING
+											// Only time one thread, as I also only time one MPI process
+											if (my_ori_particle == exp_my_first_ori_particle)
+											{
+												timer.toc(TIMING_WSUM_GETSHIFT);
+												timer.tic(TIMING_WSUM_DIFF2);
+											}
+#endif
 
 											// Store weighted sum of squared differences for sigma2_noise estimation
+											// Suggestion Robert Sinkovitz: merge difference and scale steps to make better use of cache
 											FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Mresol_fine)
 											{
 												int ires = DIRECT_MULTIDIM_ELEM(Mresol_fine, n);
 												if (ires > -1)
 												{
 													// Use FT of masked image for noise estimation!
-													double diff_real = (DIRECT_MULTIDIM_ELEM(Frefctf, n)).real - (DIRECT_MULTIDIM_ELEM(Fimg_shift, n)).real;
-													double diff_imag = (DIRECT_MULTIDIM_ELEM(Frefctf, n)).imag - (DIRECT_MULTIDIM_ELEM(Fimg_shift, n)).imag;
-													double wdiff2 = weight * (diff_real*diff_real + diff_imag*diff_imag);
-
+													DOUBLE diff_real = (DIRECT_MULTIDIM_ELEM(Frefctf, n)).real - (*(Fimg_shift + n)).real;
+													DOUBLE diff_imag = (DIRECT_MULTIDIM_ELEM(Frefctf, n)).imag - (*(Fimg_shift + n)).imag;
+													DOUBLE wdiff2 = weight * (diff_real*diff_real + diff_imag*diff_imag);
 													// group-wise sigma2_noise
 													DIRECT_MULTIDIM_ELEM(thr_wsum_sigma2_noise[group_id], ires) += wdiff2;
 													// For norm_correction
-													thr_wsum_norm_correction[ipart] += wdiff2;
+													exp_wsum_norm_correction[ipart] += wdiff2;
+                                                                                                        if (do_scale_correction && DIRECT_A1D_ELEM(mymodel.data_vs_prior_class[exp_iclass], ires) > 3.)
+                                                                                                        {
+                                                                                                            DOUBLE sumXA, sumA2;
+                                                                                                            sumXA = (DIRECT_MULTIDIM_ELEM(Frefctf, n)).real * (*(Fimg_shift + n)).real;
+                                                                                                            sumXA += (DIRECT_MULTIDIM_ELEM(Frefctf, n)).imag * (*(Fimg_shift + n)).imag;
+                                                                                                            DIRECT_A1D_ELEM(exp_wsum_scale_correction_XA[ipart], ires) += weight * sumXA;
+                                                                                                            sumA2 = (DIRECT_MULTIDIM_ELEM(Frefctf, n)).real * (DIRECT_MULTIDIM_ELEM(Frefctf, n)).real;
+                                                                                                            sumA2 += (DIRECT_MULTIDIM_ELEM(Frefctf, n)).imag * (DIRECT_MULTIDIM_ELEM(Frefctf, n)).imag;
+                                                                                                            DIRECT_A1D_ELEM(exp_wsum_scale_correction_AA[ipart], ires) += weight * sumA2;
+                                                                                                        }
 												}
 											}
-
-											// Store the weighted sums of the norm_correction terms
-											if (do_scale_correction)
+#ifdef TIMING
+											// Only time one thread, as I also only time one MPI process
+											if (my_ori_particle == exp_my_first_ori_particle)
 											{
-												double sumXA = 0.;
-												double sumA2 = 0.;
-												FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Mresol_fine)
-												{
-													int ires = DIRECT_MULTIDIM_ELEM(Mresol_fine, n);
-#ifdef DEBUG_CHECKSIZES
-													if (ires >= XSIZE(thr_wsum_scale_correction_XA[ipart]))
-													{
-														std::cerr<< "ires= "<<ires<<" XSIZE(thr_wsum_scale_correction_XA[ipart])= "<< XSIZE(thr_wsum_scale_correction_XA[ipart]) <<std::endl;
-														REPORT_ERROR("ires >= XSIZE(thr_wsum_scale_correction_XA[ipart])");
-													}
-													if (ires >= XSIZE(thr_wsum_scale_correction_AA[ipart]))
-													{
-														std::cerr<< "ires= "<<ires<<" XSIZE(thr_wsum_scale_correction_AA[ipart])= "<< XSIZE(thr_wsum_scale_correction_AA[ipart]) <<std::endl;
-														REPORT_ERROR("ires >= XSIZE(thr_wsum_scale_correction_AA[ipart])");
-													}
-#endif
-
-													// Once the reference becomes strongly regularised one does no longer want to store XA and AA!
-													if (ires > -1 && DIRECT_A1D_ELEM(mymodel.data_vs_prior_class[exp_iclass], ires) > 3.)
-													{
-														sumXA = (DIRECT_MULTIDIM_ELEM(Frefctf, n)).real * (DIRECT_MULTIDIM_ELEM(Fimg_shift, n)).real;
-														sumXA += (DIRECT_MULTIDIM_ELEM(Frefctf, n)).imag * (DIRECT_MULTIDIM_ELEM(Fimg_shift, n)).imag;
-														DIRECT_A1D_ELEM(thr_wsum_scale_correction_XA[ipart], ires) += weight * sumXA;
-
-														// This could be pre-calculated above...
-														sumA2 = (DIRECT_MULTIDIM_ELEM(Frefctf, n)).real * (DIRECT_MULTIDIM_ELEM(Frefctf, n)).real;
-														sumA2 += (DIRECT_MULTIDIM_ELEM(Frefctf, n)).imag * (DIRECT_MULTIDIM_ELEM(Frefctf, n)).imag;
-														DIRECT_A1D_ELEM(thr_wsum_scale_correction_AA[ipart], ires) += weight * sumA2;
-													}
-												}
+												timer.toc(TIMING_WSUM_DIFF2);
+												timer.tic(TIMING_WSUM_LOCALSUMS);
 											}
+#endif
 
 											// Store sum of weights for this group
 											thr_sumw_group[group_id] += weight;
-
 											// Store weights for this class and orientation
 											thr_wsum_pdf_class[exp_iclass] += weight;
 
-											if (mymodel.ref_dim ==2)
+											// The following goes MUCH faster than the original lines below....
+											if (mymodel.ref_dim == 2)
 											{
-												// Also store weighted offset differences for prior_offsets of each class
-												thr_wsum_prior_offsetx_class[exp_iclass] += weight * XX(exp_old_offset[my_image_no] + oversampled_translations[iover_trans]);
-												thr_wsum_prior_offsety_class[exp_iclass] += weight * YY(exp_old_offset[my_image_no] + oversampled_translations[iover_trans]);
-
-												// Store weighted sum2 of origin offsets (in Angstroms instead of pixels!!!)
-												thr_wsum_sigma2_offset += weight * ((mymodel.prior_offset_class[exp_iclass] - exp_old_offset[my_image_no] - oversampled_translations[iover_trans]).sum2());
-
+												thr_wsum_prior_offsetx_class[exp_iclass] += weight * (old_offset_x + oversampled_translations_x[iover_trans]);
+												thr_wsum_prior_offsety_class[exp_iclass] += weight * (old_offset_y + oversampled_translations_y[iover_trans]);
 											}
-											else
+											DOUBLE diffx = myprior_x - old_offset_x - oversampled_translations_x[iover_trans];
+											DOUBLE diffy = myprior_y - old_offset_y - oversampled_translations_y[iover_trans];
+											if (mymodel.data_dim == 3)
 											{
-												// Store weighted sum2 of origin offsets (in Angstroms instead of pixels!!!)
-												thr_wsum_sigma2_offset += weight * ((exp_prior[my_image_no] - exp_old_offset[my_image_no] - oversampled_translations[iover_trans]).sum2());
+												DOUBLE diffz  = myprior_z - old_offset_z - oversampled_translations_z[iover_trans];
+												thr_wsum_sigma2_offset += weight * (diffx*diffx + diffy*diffy + diffz*diffz);
 											}
-
-#ifdef DEBUG_CHECKSIZES
-											if (idir >= XSIZE(thr_wsum_pdf_direction[exp_iclass]))
+											else
 											{
-												std::cerr<< "idir= "<<idir<<" XSIZE(thr_wsum_pdf_direction[exp_iclass])= "<< XSIZE(thr_wsum_pdf_direction[exp_iclass]) <<std::endl;
-												REPORT_ERROR("idir >= XSIZE(thr_wsum_pdf_direction[iclass])");
+												thr_wsum_sigma2_offset += weight * (diffx*diffx + diffy*diffy);
 											}
-#endif
 
 											// Store weight for this direction of this class
-											if (mymodel.orientational_prior_mode == NOPRIOR)
+											if (do_skip_align || do_skip_rotate )
+											{
+												//ignore pdf_direction
+											}
+											else if (mymodel.orientational_prior_mode == NOPRIOR)
 											{
+#ifdef DEBUG_CHECKSIZES
+												if (idir >= XSIZE(thr_wsum_pdf_direction[exp_iclass]))
+												{
+													std::cerr<< "idir= "<<idir<<" XSIZE(thr_wsum_pdf_direction[exp_iclass])= "<< XSIZE(thr_wsum_pdf_direction[exp_iclass]) <<std::endl;
+													REPORT_ERROR("idir >= XSIZE(thr_wsum_pdf_direction[iclass])");
+												}
+#endif
 												DIRECT_MULTIDIM_ELEM(thr_wsum_pdf_direction[exp_iclass], idir) += weight;
 											}
 											else
 											{
 												// In the case of orientational priors, get the original number of the direction back
-												long int mydir = sampling.getDirectionNumberAlsoZeroPrior(idir);
+												long int mydir = exp_pointer_dir_nonzeroprior[idir];
 												DIRECT_MULTIDIM_ELEM(thr_wsum_pdf_direction[exp_iclass], mydir) += weight;
 											}
 
 #ifdef TIMING
 											// Only time one thread, as I also only time one MPI process
-											if (thread_id == 0)
-												timer.toc(TIMING_WSUM_DIFF2);
-											// Only time one thread, as I also only time one MPI process
-											if (thread_id == 0)
+											if (my_ori_particle == exp_my_first_ori_particle)
+											{
+												timer.toc(TIMING_WSUM_LOCALSUMS);
 												timer.tic(TIMING_WSUM_SUMSHIFT);
+											}
 #endif
-
 											// Store sum of weight*SSNR*Fimg in data and sum of weight*SSNR in weight
 											// Use the FT of the unmasked image to back-project in order to prevent reconstruction artefacts! SS 25oct11
-											FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Fimg_shift)
+											FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Fimg)
 											{
-												double myctf = DIRECT_MULTIDIM_ELEM(Mctf, n);
+												DOUBLE myctf = DIRECT_MULTIDIM_ELEM(Mctf, n);
 												// Note that weightxinvsigma2 already contains the CTF!
-												double weightxinvsigma2 = weight * myctf * DIRECT_MULTIDIM_ELEM(Minvsigma2, n);
+												DOUBLE weightxinvsigma2 = weight * myctf * DIRECT_MULTIDIM_ELEM(Minvsigma2, n);
 												// now Fimg stores sum of all shifted w*Fimg
-												(DIRECT_MULTIDIM_ELEM(Fimg, n)).real += (DIRECT_MULTIDIM_ELEM(Fimg_shift_nomask, n)).real * weightxinvsigma2;
-												(DIRECT_MULTIDIM_ELEM(Fimg, n)).imag += (DIRECT_MULTIDIM_ELEM(Fimg_shift_nomask, n)).imag * weightxinvsigma2;
+												(DIRECT_MULTIDIM_ELEM(Fimg, n)).real += (*(Fimg_shift_nomask + n)).real * weightxinvsigma2;
+												(DIRECT_MULTIDIM_ELEM(Fimg, n)).imag += (*(Fimg_shift_nomask + n)).imag * weightxinvsigma2;
 												// now Fweight stores sum of all w
 												// Note that CTF needs to be squared in Fweight, weightxinvsigma2 already contained one copy
 												DIRECT_MULTIDIM_ELEM(Fweight, n) += weightxinvsigma2 * myctf;
 											}
-
 #ifdef TIMING
 											// Only time one thread, as I also only time one MPI process
-											if (thread_id == 0)
+											if (my_ori_particle == exp_my_first_ori_particle)
 												timer.toc(TIMING_WSUM_SUMSHIFT);
 #endif
-
 										} // end if !do_skip_maximization
 
 										// Keep track of max_weight and the corresponding optimal hidden variables
-										if (weight > thr_max_weight[ipart])
+										if (weight > exp_max_weight[ipart])
 										{
 											// Store optimal image parameters
-											thr_max_weight[ipart] = weight;
-
-											// Calculate the angles back from the Euler matrix because for tilt series exp_R_mic may have changed them...
-											//std::cerr << " ORI rot= " << rot << " tilt= " << tilt << " psi= " << psi << std::endl;
-											Euler_matrix2angles(A.inv(), rot, tilt, psi);
-											//std::cerr << " BACK rot= " << rot << " tilt= " << tilt << " psi= " << psi << std::endl;
-
-											DIRECT_A2D_ELEM(thr_metadata, my_image_no, METADATA_ROT) = rot;
-											DIRECT_A2D_ELEM(thr_metadata, my_image_no, METADATA_TILT) = tilt;
-											DIRECT_A2D_ELEM(thr_metadata, my_image_no, METADATA_PSI) = psi;
-											DIRECT_A2D_ELEM(thr_metadata, my_image_no, METADATA_XOFF) = XX(exp_old_offset[my_image_no]) + XX(oversampled_translations[iover_trans]);
-											DIRECT_A2D_ELEM(thr_metadata, my_image_no, METADATA_YOFF) = YY(exp_old_offset[my_image_no]) + YY(oversampled_translations[iover_trans]);
-											DIRECT_A2D_ELEM(thr_metadata, my_image_no, METADATA_CLASS) = (double)exp_iclass + 1;
-											DIRECT_A2D_ELEM(thr_metadata, my_image_no, METADATA_PMAX) = thr_max_weight[ipart];
+											exp_max_weight[ipart] = weight;
+
+											// TODO: remove, for now to maintain exact numerical version of old threads....
+											A = A.inv();
+											A = A.inv();
+											Euler_matrix2angles(A, rot, tilt, psi);
+
+											DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_ROT) = rot;
+											DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_TILT) = tilt;
+											DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_PSI) = psi;
+											DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_XOFF) = XX(exp_old_offset[ipart]) + oversampled_translations_x[iover_trans];
+											DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_YOFF) = YY(exp_old_offset[ipart]) + oversampled_translations_y[iover_trans];
+											if (mymodel.data_dim == 3)
+												DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_ZOFF) = ZZ(exp_old_offset[ipart]) + oversampled_translations_z[iover_trans];
+											DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_CLASS) = (DOUBLE)exp_iclass + 1;
+											DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_PMAX) = exp_max_weight[ipart];
 										}
-
 									} // end if weight >= exp_significant_weight
 								} // end loop iover_trans
 							} // end loop itrans
-						}// end loop part_id (i)
-					} // end loop ori_part_id
-
-					if (!do_skip_maximization)
-					{
-#ifdef TIMING
-						// Only time one thread, as I also only time one MPI process
-						if (thread_id == 0)
-							timer.tic(TIMING_WSUM_BACKPROJ);
-#endif
-						// Perform the actual back-projection.
-						// This is done with the sum of all (in-plane) shifted Fimg's
-						// Perform this inside a mutex
-						global_mutex2.lock();
-						(wsum_model.BPref[exp_iclass]).set2DFourierTransform(Fimg, A, IS_INV, &Fweight);
-						global_mutex2.unlock();
+						} // end loop ipart
 
+						if (!do_skip_maximization)
+						{
 #ifdef TIMING
-						// Only time one thread, as I also only time one MPI process
-						if (thread_id == 0)
-							timer.toc(TIMING_WSUM_BACKPROJ);
-#endif
-					} // end if !do_skip_maximization
-
-				}// end if iover_rot
-			}// end loop do_proceed
-
-		} // end loop ipsi
-	} // end loop idir
-
-	// Now, inside a global_mutex, update the weighted sums among all threads
-	global_mutex.lock();
-
-	if (!do_skip_maximization)
-	{
-		if (do_scale_correction)
-		{
-			for (int n = 0; n < exp_nr_particles; n++)
-			{
-				exp_wsum_scale_correction_XA[n] += thr_wsum_scale_correction_XA[n];
-				exp_wsum_scale_correction_AA[n] += thr_wsum_scale_correction_AA[n];
-			}
-		}
-		for (int n = 0; n < exp_nr_particles; n++)
-		{
-			exp_wsum_norm_correction[n] += thr_wsum_norm_correction[n];
-		}
-		for (int n = 0; n < mymodel.nr_groups; n++)
-		{
-			wsum_model.sigma2_noise[n] += thr_wsum_sigma2_noise[n];
-			wsum_model.sumw_group[n] += thr_sumw_group[n];
-		}
-		for (int n = 0; n < mymodel.nr_classes; n++)
-		{
-			wsum_model.pdf_class[n] += thr_wsum_pdf_class[n];
-
-			if (mymodel.ref_dim == 2)
-			{
-				XX(wsum_model.prior_offset_class[n]) += thr_wsum_prior_offsetx_class[n];
-				YY(wsum_model.prior_offset_class[n]) += thr_wsum_prior_offsety_class[n];
-			}
-#ifdef CHECKSIZES
-			if (XSIZE(wsum_model.pdf_direction[n]) != XSIZE(thr_wsum_pdf_direction[n]))
-			{
-				std::cerr << " XSIZE(wsum_model.pdf_direction[n])= " << XSIZE(wsum_model.pdf_direction[n]) << " XSIZE(thr_wsum_pdf_direction[n])= " << XSIZE(thr_wsum_pdf_direction[n]) << std::endl;
-				REPORT_ERROR("XSIZE(wsum_model.pdf_direction[n]) != XSIZE(thr_wsum_pdf_direction[n])");
-			}
-#endif
-			wsum_model.pdf_direction[n] += thr_wsum_pdf_direction[n];
-		}
-		wsum_model.sigma2_offset += thr_wsum_sigma2_offset;
-	} // end if !do_skip_maximization
-
-	// Check max_weight for each particle and set exp_metadata
-	for (int n = 0; n < exp_nr_particles; n++)
-	{
-		// Equal-to because of the series: the nth images in a series will have the same maximum as the first one
-		if (thr_max_weight[n] >= exp_max_weight[n])
-		{
-			// Set max_weight
-			exp_max_weight[n] = thr_max_weight[n];
-
-			// Set metadata
-			long int my_image_no = exp_starting_image_no[n] + exp_iseries;
-			DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ROT)  = DIRECT_A2D_ELEM(thr_metadata, my_image_no, METADATA_ROT);
-			DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_TILT) = DIRECT_A2D_ELEM(thr_metadata, my_image_no, METADATA_TILT);
-			DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PSI)  = DIRECT_A2D_ELEM(thr_metadata, my_image_no, METADATA_PSI);
-			DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_XOFF) = DIRECT_A2D_ELEM(thr_metadata, my_image_no, METADATA_XOFF);
-			DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_YOFF) = DIRECT_A2D_ELEM(thr_metadata, my_image_no, METADATA_YOFF);
-			DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CLASS)= DIRECT_A2D_ELEM(thr_metadata, my_image_no, METADATA_CLASS);
-			DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PMAX) = DIRECT_A2D_ELEM(thr_metadata, my_image_no, METADATA_PMAX);
-		}
-	}
-	global_mutex.unlock();
-
-	// Wait until all threads have finished
-	global_barrier->wait();
-#ifdef DEBUG_THREAD
-    std::cerr << "leaving doThreadStoreWeightedSumsAllOrientations" << std::endl;
-#endif
-
-}
-
-void MlOptimiser::storeWeightedSums()
-{
-
+							// Only time one thread, as I also only time one MPI process
+							if (my_ori_particle == exp_my_first_ori_particle)
+								timer.tic(TIMING_WSUM_BACKPROJ);
+#endif
+							// Perform the actual back-projection.
+							// This is done with the sum of all (in-plane) shifted Fimg's
+							// Perform this inside a mutex
+							int my_mutex = exp_iclass % NR_CLASS_MUTEXES;
+							pthread_mutex_lock(&global_mutex2[my_mutex]);
+							(wsum_model.BPref[exp_iclass]).set2DFourierTransform(Fimg, A, IS_NOT_INV, &Fweight);
+							pthread_mutex_unlock(&global_mutex2[my_mutex]);
 #ifdef TIMING
-	timer.tic(TIMING_ESP_WSUM);
-#endif
+							// Only time one thread, as I also only time one MPI process
+							if (my_ori_particle == exp_my_first_ori_particle)
+								timer.toc(TIMING_WSUM_BACKPROJ);
+#endif
+						} // end if !do_skip_maximization
+					} // end loop iover_rot
+				}// end loop do_proceed
+			} // end loop ipsi
+		} // end loop idir
+	} // end loop iclass
 
-	// Initialise the maximum of all weights to a negative value
-	exp_max_weight.resize(exp_nr_particles);
-	for (int n = 0; n < exp_nr_particles; n++)
-		exp_max_weight[n] = -1.;
 
-	// In doThreadPrecalculateShiftedImagesCtfsAndInvSigma2s() the origin of the exp_local_Minvsigma2s was omitted.
-	// Set those back here
-	for (long int ori_part_id = exp_my_first_ori_particle, ipart = 0; ori_part_id <= exp_my_last_ori_particle; ori_part_id++)
+	// Extend norm_correction and sigma2_noise estimation to higher resolutions for all particles
+	// Also calculate dLL for each particle and store in metadata
+	// loop over all particles inside this ori_particle
+	DOUBLE thr_avg_norm_correction = 0.;
+	DOUBLE thr_sum_dLL = 0., thr_sum_Pmax = 0.;
+	for (long int ipart = 0; ipart < mydata.ori_particles[my_ori_particle].particles_id.size(); ipart++)
 	{
-		// loop over all particles inside this ori_particle
-		for (long int i = 0; i < mydata.ori_particles[ori_part_id].particles_id.size(); i++, ipart++)
-		{
-			long int part_id = mydata.ori_particles[ori_part_id].particles_id[i];
-
-			for (exp_iseries = 0; exp_iseries < mydata.getNrImagesInSeries(part_id); exp_iseries++)
-			{
-				// Re-get all shifted versions of the (current_sized) images, their (current_sized) CTFs and their inverted Sigma2 matrices
-				// This may be necessary for when using --strict_highres_exp. Otherwise norm estimation may become unstable!!
-				exp_ipart_ThreadTaskDistributor->reset(); // reset thread distribution tasks
-				global_ThreadManager->run(globalThreadPrecalculateShiftedImagesCtfsAndInvSigma2s);
-
-				int group_id = mydata.getGroupId(part_id, exp_iseries);
-				int my_image_no = exp_starting_image_no[ipart] + exp_iseries;
-				DIRECT_MULTIDIM_ELEM(exp_local_Minvsigma2s[my_image_no], 0) = 1. / (sigma2_fudge * DIRECT_A1D_ELEM(mymodel.sigma2_noise[group_id], 0));
-			}
-		}
-	}
+		long int part_id = mydata.ori_particles[my_ori_particle].particles_id[ipart];
+		int group_id = mydata.getGroupId(part_id);
 
-	for (exp_iseries = 0; exp_iseries < mydata.getNrImagesInSeries((mydata.ori_particles[exp_my_first_ori_particle]).particles_id[0]); exp_iseries++)
-	{
-		// TODO: check this!!!
-		// I think this is just done for the first ipart
-		int my_image_no = exp_starting_image_no[0] + exp_iseries;
-		// Get micrograph transformation matrix
-		exp_R_mic.resize(3,3);
-		exp_R_mic(0,0) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_0_0);
-		exp_R_mic(0,1) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_0_1);
-		exp_R_mic(0,2) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_0_2);
-		exp_R_mic(1,0) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_1_0);
-		exp_R_mic(1,1) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_1_1);
-		exp_R_mic(1,2) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_1_2);
-		exp_R_mic(2,0) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_2_0);
-		exp_R_mic(2,1) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_2_1);
-		exp_R_mic(2,2) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_2_2);
-
-		// For norm_correction of this iseries image:
-		exp_wsum_norm_correction.resize(exp_nr_particles);
-		for (int n = 0; n < exp_nr_particles; n++)
-			exp_wsum_norm_correction[n] = 0.;
-
-		// For scale_correction of this iseries image:
-		if (do_scale_correction)
+		// If the current images were smaller than the original size, fill the rest of wsum_model.sigma2_noise with the power_class spectrum of the images
+		for (int ires = mymodel.current_size/2 + 1; ires < mymodel.ori_size/2 + 1; ires++)
 		{
-			MultidimArray<double> aux;
-			aux.initZeros(mymodel.ori_size/2 + 1);
-			exp_wsum_scale_correction_XA.resize(exp_nr_particles);
-			exp_wsum_scale_correction_AA.resize(exp_nr_particles);
-			for (int n = 0; n < exp_nr_particles; n++)
-			{
-				exp_wsum_scale_correction_XA[n] = aux;
-				exp_wsum_scale_correction_AA[n] = aux;
-			}
+			DIRECT_A1D_ELEM(thr_wsum_sigma2_noise[group_id], ires) += DIRECT_A1D_ELEM(exp_power_imgs[ipart], ires);
+			// Also extend the weighted sum of the norm_correction
+			exp_wsum_norm_correction[ipart] += DIRECT_A1D_ELEM(exp_power_imgs[ipart], ires);
 		}
-
-		// Loop from iclass_min to iclass_max to deal with seed generation in first iteration
-		for (exp_iclass = iclass_min; exp_iclass <= iclass_max; exp_iclass++)
-		{
-
-			// The loops over all orientations are parallelised using threads
-			exp_iorient_ThreadTaskDistributor->reset(); // reset thread distribution tasks
-			global_ThreadManager->run(globalThreadStoreWeightedSumsAllOrientations);
-
-		} // end loop iclass
-
-		// Extend norm_correction and sigma2_noise estimation to higher resolutions for all particles
-		for (long int ori_part_id = exp_my_first_ori_particle, ipart = 0; ori_part_id <= exp_my_last_ori_particle; ori_part_id++)
-		{
-			// loop over all particles inside this ori_particle
-			for (long int i = 0; i < mydata.ori_particles[ori_part_id].particles_id.size(); i++, ipart++)
-			{
-				long int part_id = mydata.ori_particles[ori_part_id].particles_id[i];
-
-				// Which number was this image in the combined array of exp_iseries and exp_part_id
-				long int my_image_no = exp_starting_image_no[ipart] + exp_iseries;
-
-				// If the current images were smaller than the original size, fill the rest of wsum_model.sigma2_noise with the power_class spectrum of the images
-				int group_id = mydata.getGroupId(part_id, exp_iseries);
-				for (int ires = mymodel.current_size/2 + 1; ires < mymodel.ori_size/2 + 1; ires++)
-				{
-					DIRECT_A1D_ELEM(wsum_model.sigma2_noise[group_id], ires) += DIRECT_A1D_ELEM(exp_power_imgs[my_image_no], ires);
-					// Also extend the weighted sum of the norm_correction
-					exp_wsum_norm_correction[ipart] += DIRECT_A1D_ELEM(exp_power_imgs[my_image_no], ires);
-				}
-
-				// Store norm_correction
-				// Multiply by old value because the old norm_correction term was already applied to the image
-				if (do_norm_correction)
-				{
-					double old_norm_correction = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NORM);
-					old_norm_correction /= mymodel.avg_norm_correction;
-					// Now set the new norm_correction in the relevant position of exp_metadata
-					// The factor two below is because exp_wsum_norm_correctiom is similar to sigma2_noise, which is the variance for the real/imag components
-					// The variance of the total image (on which one normalizes) is twice this value!
-					DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NORM) = old_norm_correction * sqrt(exp_wsum_norm_correction[ipart] * 2.);
-					wsum_model.avg_norm_correction += old_norm_correction * sqrt(exp_wsum_norm_correction[ipart] * 2.);
-
-					if (!(iter == 1 && do_firstiter_cc) && DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NORM) > 10.)
-					{
-						std::cout << " WARNING: norm_correction= "<< DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NORM) << " for particle " << part_id << " in group " << group_id + 1 << "; Are your groups large enough?" << std::endl;
-						std::cout << " mymodel.current_size= " << mymodel.current_size << " mymodel.ori_size= " << mymodel.ori_size << " part_id= " << part_id << std::endl;
-						std::cout << " coarse_size= " << coarse_size << std::endl;
-						std::cout << " DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NORM)= " << DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NORM) << std::endl;
-						std::cout << " mymodel.avg_norm_correction= " << mymodel.avg_norm_correction << std::endl;
-						std::cout << " exp_wsum_norm_correction[ipart]= " << exp_wsum_norm_correction[ipart] << std::endl;
-						std::cout << " old_norm_correction= " << old_norm_correction << std::endl;
-						std::cout << " wsum_model.avg_norm_correction= " << wsum_model.avg_norm_correction << std::endl;
-						std::cout << " group_id= " << group_id << " mymodel.scale_correction[group_id]= " << mymodel.scale_correction[group_id] << std::endl;
-						std::cout << " mymodel.sigma2_noise[group_id]= " << mymodel.sigma2_noise[group_id] << std::endl;
-						std::cout << " wsum_model.sigma2_noise[group_id]= " << wsum_model.sigma2_noise[group_id] << std::endl;
-						std::cout << " exp_power_imgs[my_image_no]= " << exp_power_imgs[my_image_no] << std::endl;
-						std::cout << " exp_wsum_scale_correction_XA[ipart]= " << exp_wsum_scale_correction_XA[ipart] << " exp_wsum_scale_correction_AA[ipart]= " << exp_wsum_scale_correction_AA[ipart] << std::endl;
-						std::cout << " wsum_model.wsum_signal_product_spectra[group_id]= " << wsum_model.wsum_signal_product_spectra[group_id] << " wsum_model.wsum_reference_power_spectra[group_id]= " << wsum_model.wsum_reference_power_spectra[group_id] << std::endl;
-						std::cout << " exp_min_diff2[ipart]= " << exp_min_diff2[ipart] << std::endl;
-						std::cout << " ml_model.scale_correction[group_id]= " << mymodel.scale_correction[group_id] << std::endl;
-						std::cout << " exp_significant_weight[ipart]= " << exp_significant_weight[ipart] << std::endl;
-						std::cout << " exp_max_weight[ipart]= " << exp_max_weight[ipart] << std::endl;
-
-					}
-					//TMP DEBUGGING
-					/*
-					if (!(iter == 1 && do_firstiter_cc) && DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NORM) > 10.)
-					{
-						std::cerr << " mymodel.current_size= " << mymodel.current_size << " mymodel.ori_size= " << mymodel.ori_size << " part_id= " << part_id << std::endl;
-						std::cerr << " DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NORM)= " << DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NORM) << std::endl;
-						std::cerr << " mymodel.avg_norm_correction= " << mymodel.avg_norm_correction << std::endl;
-						std::cerr << " exp_wsum_norm_correction[ipart]= " << exp_wsum_norm_correction[ipart] << std::endl;
-						std::cerr << " old_norm_correction= " << old_norm_correction << std::endl;
-						std::cerr << " wsum_model.avg_norm_correction= " << wsum_model.avg_norm_correction << std::endl;
-						std::cerr << " group_id= " << group_id << " mymodel.scale_correction[group_id]= " << mymodel.scale_correction[group_id] << std::endl;
-						std::cerr << " mymodel.sigma2_noise[group_id]= " << mymodel.sigma2_noise[group_id] << std::endl;
-						std::cerr << " wsum_model.sigma2_noise[group_id]= " << wsum_model.sigma2_noise[group_id] << std::endl;
-						std::cerr << " exp_power_imgs[my_image_no]= " << exp_power_imgs[my_image_no] << std::endl;
-						std::cerr << " exp_wsum_scale_correction_XA[ipart]= " << exp_wsum_scale_correction_XA[ipart] << " exp_wsum_scale_correction_AA[ipart]= " << exp_wsum_scale_correction_AA[ipart] << std::endl;
-						std::cerr << " wsum_model.wsum_signal_product_spectra[group_id]= " << wsum_model.wsum_signal_product_spectra[group_id] << " wsum_model.wsum_reference_power_spectra[group_id]= " << wsum_model.wsum_reference_power_spectra[group_id] << std::endl;
-						std::cerr << " exp_min_diff2[ipart]= " << exp_min_diff2[ipart] << std::endl;
-						std::cerr << " ml_model.scale_correction[group_id]= " << mymodel.scale_correction[group_id] << std::endl;
-						std::cerr << " exp_significant_weight[ipart]= " << exp_significant_weight[ipart] << std::endl;
-						std::cerr << " exp_max_weight[ipart]= " << exp_max_weight[ipart] << std::endl;
-						mymodel.write("debug");
-						std::cerr << "written debug_model.star" << std::endl;
-						REPORT_ERROR("MlOptimiser::storeWeightedSums ERROR: normalization is larger than 10");
-					}
-					*/
-
-				}
-
-				// Store weighted sums for scale_correction
-				if (do_scale_correction)
-				{
-					// Divide XA by the old scale_correction and AA by the square of that, because was incorporated into Fctf
-					exp_wsum_scale_correction_XA[ipart] /= mymodel.scale_correction[group_id];
-					exp_wsum_scale_correction_AA[ipart] /= mymodel.scale_correction[group_id] * mymodel.scale_correction[group_id];
-
-					wsum_model.wsum_signal_product_spectra[group_id] += exp_wsum_scale_correction_XA[ipart];
-					wsum_model.wsum_reference_power_spectra[group_id] += exp_wsum_scale_correction_AA[ipart];
-				}
-
-			} // end loop part_id (i)
-		} // end loop ori_part_id
-	} // end loop exp_iseries
-
-
-#ifdef DEBUG_OVERSAMPLING
-	std::cerr << " max_weight= " << max_weight << " nr_sign_sam= "<<nr_significant_samples<<" sign w= "<<exp_significant_weight<<std::endl;
-#endif
-
-	// Some analytics...
-	// Calculate normalization constant for dLL
-	for (long int ori_part_id = exp_my_first_ori_particle, ipart = 0; ori_part_id <= exp_my_last_ori_particle; ori_part_id++)
-	{
-		// loop over all particles inside this ori_particle
-		for (long int i = 0; i < mydata.ori_particles[ori_part_id].particles_id.size(); i++, ipart++)
+
+		// Store norm_correction
+		// Multiply by old value because the old norm_correction term was already applied to the image
+		if (do_norm_correction)
 		{
-			long int part_id = mydata.ori_particles[ori_part_id].particles_id[i];
-			double logsigma2 = 0.;
-			for (long int iseries = 0; iseries < mydata.getNrImagesInSeries(part_id); iseries++)
+			DOUBLE old_norm_correction = DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_NORM);
+			old_norm_correction /= mymodel.avg_norm_correction;
+			// The factor two below is because exp_wsum_norm_correctiom is similar to sigma2_noise, which is the variance for the real/imag components
+			// The variance of the total image (on which one normalizes) is twice this value!
+			DOUBLE normcorr = old_norm_correction * sqrt(exp_wsum_norm_correction[ipart] * 2.);
+			thr_avg_norm_correction += normcorr;
+			// Now set the new norm_correction in the relevant position of exp_metadata
+			DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_NORM) = normcorr;
+
+			// Print warning for strange norm-correction values
+			if (!(iter == 1 && do_firstiter_cc) && DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_NORM) > 10.)
 			{
-				int group_id = mydata.getGroupId(part_id, iseries);
-				FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Mresol_fine)
-				{
-					int ires = DIRECT_MULTIDIM_ELEM(Mresol_fine, n);
-					// Note there is no sqrt in the normalisation term because of the 2-dimensionality of the complex-plane
-					// Also exclude origin from logsigma2, as this will not be considered in the P-calculations
-					if (ires > 0)
-						logsigma2 += log( 2. * PI * DIRECT_A1D_ELEM(mymodel.sigma2_noise[group_id], ires));
-				}
-
+				std::cout << " WARNING: norm_correction= "<< DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_NORM) << " for particle " << part_id << " in group " << group_id + 1 << "; Are your groups large enough?" << std::endl;
 			}
 
-			if (exp_sum_weight[ipart]==0)
+			//TMP DEBUGGING
+			/*
+			if (!(iter == 1 && do_firstiter_cc) && DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NORM) > 10.)
 			{
-				std::cerr << " part_id= " << part_id << std::endl;
-				std::cerr << " ipart= " << ipart << std::endl;
+				std::cerr << " mymodel.current_size= " << mymodel.current_size << " mymodel.ori_size= " << mymodel.ori_size << " part_id= " << part_id << std::endl;
+				std::cerr << " DIRECT_A2D_ELEM(exp_metadata, ipart, METADATA_NORM)= " << DIRECT_A2D_ELEM(exp_metadata, ipart, METADATA_NORM) << std::endl;
+				std::cerr << " mymodel.avg_norm_correction= " << mymodel.avg_norm_correction << std::endl;
+				std::cerr << " exp_wsum_norm_correction[ipart]= " << exp_wsum_norm_correction[ipart] << std::endl;
+				std::cerr << " old_norm_correction= " << old_norm_correction << std::endl;
+				std::cerr << " wsum_model.avg_norm_correction= " << wsum_model.avg_norm_correction << std::endl;
+				std::cerr << " group_id= " << group_id << " mymodel.scale_correction[group_id]= " << mymodel.scale_correction[group_id] << std::endl;
+				std::cerr << " mymodel.sigma2_noise[group_id]= " << mymodel.sigma2_noise[group_id] << std::endl;
+				std::cerr << " wsum_model.sigma2_noise[group_id]= " << wsum_model.sigma2_noise[group_id] << std::endl;
+				std::cerr << " exp_power_imgs[ipart]= " << exp_power_imgs[ipart] << std::endl;
+				std::cerr << " exp_wsum_scale_correction_XA[ipart]= " << exp_wsum_scale_correction_XA[ipart] << " exp_wsum_scale_correction_AA[ipart]= " << exp_wsum_scale_correction_AA[ipart] << std::endl;
+				std::cerr << " wsum_model.wsum_signal_product_spectra[group_id]= " << wsum_model.wsum_signal_product_spectra[group_id] << " wsum_model.wsum_reference_power_spectra[group_id]= " << wsum_model.wsum_reference_power_spectra[group_id] << std::endl;
 				std::cerr << " exp_min_diff2[ipart]= " << exp_min_diff2[ipart] << std::endl;
-				std::cerr << " logsigma2= " << logsigma2 << std::endl;
-				int group_id = mydata.getGroupId(part_id, 0);
-				std::cerr << " group_id= " << group_id << std::endl;
 				std::cerr << " ml_model.scale_correction[group_id]= " << mymodel.scale_correction[group_id] << std::endl;
 				std::cerr << " exp_significant_weight[ipart]= " << exp_significant_weight[ipart] << std::endl;
 				std::cerr << " exp_max_weight[ipart]= " << exp_max_weight[ipart] << std::endl;
-				std::cerr << " ml_model.sigma2_noise[group_id]= " << mymodel.sigma2_noise[group_id] << std::endl;
-				REPORT_ERROR("ERROR: exp_sum_weight[ipart]==0");
+				mymodel.write("debug");
+				std::cerr << "written debug_model.star" << std::endl;
+				REPORT_ERROR("MlOptimiser::storeWeightedSums ERROR: normalization is larger than 10");
 			}
+			*/
+
+		}
 
-			double dLL;
+		// Store weighted sums for scale_correction
+		if (do_scale_correction)
+		{
+			// Divide XA by the old scale_correction and AA by the square of that, because was incorporated into Fctf
+			exp_wsum_scale_correction_XA[ipart] /= mymodel.scale_correction[group_id];
+			exp_wsum_scale_correction_AA[ipart] /= mymodel.scale_correction[group_id] * mymodel.scale_correction[group_id];
 
-			if ((iter==1 && do_firstiter_cc) || do_always_cc)
-				dLL = -exp_min_diff2[ipart];
-			else
-				dLL = log(exp_sum_weight[ipart]) - exp_min_diff2[ipart] - logsigma2;
+			thr_wsum_signal_product_spectra[group_id] += exp_wsum_scale_correction_XA[ipart];
+			thr_wsum_reference_power_spectra[group_id] += exp_wsum_scale_correction_AA[ipart];
+		}
+
+		// Calculate DLL for each particle
+		DOUBLE logsigma2 = 0.;
+		FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Mresol_fine)
+		{
+			int ires = DIRECT_MULTIDIM_ELEM(Mresol_fine, n);
+			// Note there is no sqrt in the normalisation term because of the 2-dimensionality of the complex-plane
+			// Also exclude origin from logsigma2, as this will not be considered in the P-calculations
+			if (ires > 0)
+				logsigma2 += log( 2. * PI * DIRECT_A1D_ELEM(mymodel.sigma2_noise[group_id], ires));
+		}
+		if (exp_sum_weight[ipart]==0)
+		{
+			std::cerr << " part_id= " << part_id << std::endl;
+			std::cerr << " ipart= " << ipart << std::endl;
+			std::cerr << " exp_min_diff2[ipart]= " << exp_min_diff2[ipart] << std::endl;
+			std::cerr << " logsigma2= " << logsigma2 << std::endl;
+			int group_id = mydata.getGroupId(part_id);
+			std::cerr << " group_id= " << group_id << std::endl;
+			std::cerr << " ml_model.scale_correction[group_id]= " << mymodel.scale_correction[group_id] << std::endl;
+			std::cerr << " exp_significant_weight[ipart]= " << exp_significant_weight[ipart] << std::endl;
+			std::cerr << " exp_max_weight[ipart]= " << exp_max_weight[ipart] << std::endl;
+			std::cerr << " ml_model.sigma2_noise[group_id]= " << mymodel.sigma2_noise[group_id] << std::endl;
+			REPORT_ERROR("ERROR: exp_sum_weight[ipart]==0");
+		}
+		DOUBLE dLL;
+		if ((iter==1 && do_firstiter_cc) || do_always_cc)
+			dLL = -exp_min_diff2[ipart];
+		else
+			dLL = log(exp_sum_weight[ipart]) - exp_min_diff2[ipart] - logsigma2;
+
+		// Store dLL of each image in the output array, and keep track of total sum
+		DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_DLL) = dLL;
+		thr_sum_dLL += dLL;
 
-			wsum_model.LL += dLL;
-			wsum_model.ave_Pmax += DIRECT_A2D_ELEM(exp_metadata, exp_starting_image_no[ipart], METADATA_PMAX);
+		// Also store sum of Pmax
+		thr_sum_Pmax += DIRECT_A2D_ELEM(exp_metadata, metadata_offset + ipart, METADATA_PMAX);
+
+	}
 
-			// Also store dLL of each image in the output array
-			for (long int iseries = 0; iseries < mydata.getNrImagesInSeries(part_id); iseries++)
+	// Now, inside a global_mutex, update the other weighted sums among all threads
+	if (!do_skip_maximization)
+	{
+		pthread_mutex_lock(&global_mutex);
+		for (int n = 0; n < mymodel.nr_groups; n++)
+		{
+			wsum_model.sigma2_noise[n] += thr_wsum_sigma2_noise[n];
+			wsum_model.sumw_group[n] += thr_sumw_group[n];
+			if (do_scale_correction)
 			{
-				long int my_image_no = exp_starting_image_no[ipart] + iseries;
-				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_DLL) = dLL;
+				wsum_model.wsum_signal_product_spectra[n] += thr_wsum_signal_product_spectra[n];
+				wsum_model.wsum_reference_power_spectra[n] += thr_wsum_reference_power_spectra[n];
 			}
-		} // end loop part_id
-	} // end loop ori_part_id
+		}
+		for (int n = 0; n < mymodel.nr_classes; n++)
+		{
+			wsum_model.pdf_class[n] += thr_wsum_pdf_class[n];
+			if (mymodel.ref_dim == 2)
+			{
+				XX(wsum_model.prior_offset_class[n]) += thr_wsum_prior_offsetx_class[n];
+				YY(wsum_model.prior_offset_class[n]) += thr_wsum_prior_offsety_class[n];
+			}
+#ifdef CHECKSIZES
+			if (XSIZE(wsum_model.pdf_direction[n]) != XSIZE(thr_wsum_pdf_direction[n]))
+			{
+				std::cerr << " XSIZE(wsum_model.pdf_direction[n])= " << XSIZE(wsum_model.pdf_direction[n]) << " XSIZE(thr_wsum_pdf_direction[n])= " << XSIZE(thr_wsum_pdf_direction[n]) << std::endl;
+				REPORT_ERROR("XSIZE(wsum_model.pdf_direction[n]) != XSIZE(thr_wsum_pdf_direction[n])");
+			}
+#endif
+			if (!(do_skip_align || do_skip_rotate) )
+				wsum_model.pdf_direction[n] += thr_wsum_pdf_direction[n];
+		}
+		wsum_model.sigma2_offset += thr_wsum_sigma2_offset;
+		if (do_norm_correction)
+			wsum_model.avg_norm_correction += thr_avg_norm_correction;
+		wsum_model.LL += thr_sum_dLL;
+		wsum_model.ave_Pmax += thr_sum_Pmax;
+		pthread_mutex_unlock(&global_mutex);
+	} // end if !do_skip_maximization
+
+
+#ifdef DEBUG_OVERSAMPLING
+	std::cerr << " max_weight= " << max_weight << " nr_sign_sam= "<<nr_significant_samples<<" sign w= "<<exp_significant_weight<<std::endl;
+#endif
+
 #ifdef TIMING
-	timer.toc(TIMING_ESP_WSUM);
+	if (my_ori_particle == exp_my_first_ori_particle)
+		timer.toc(TIMING_ESP_WSUM);
 #endif
 
 }
@@ -4919,52 +4939,52 @@ void MlOptimiser::monitorHiddenVariableChanges(long int my_first_ori_particle, l
 #endif
 
 		// loop over all particles inside this ori_particle
-		for (long int i = 0; i < mydata.ori_particles[ori_part_id].particles_id.size(); i++)
+		for (long int ipart = 0; ipart < mydata.ori_particles[ori_part_id].particles_id.size(); ipart++, my_image_no++)
 		{
-			long int part_id = mydata.ori_particles[ori_part_id].particles_id[i];
-			for (int iseries = 0; iseries < mydata.getNrImagesInSeries(part_id); iseries++, my_image_no++)
-			{
-				long int img_id = mydata.getImageId(part_id, iseries);
+			long int part_id = mydata.ori_particles[ori_part_id].particles_id[ipart];
 
 #ifdef DEBUG_CHECKSIZES
-				if (img_id >= mydata.MDimg.numberOfObjects())
-				{
-					std::cerr<< "img_id= "<<img_id<<" mydata.MDimg.numberOfObjects()= "<< mydata.MDimg.numberOfObjects() <<std::endl;
-					REPORT_ERROR("img_id >= mydata.MDimg.numberOfObjects()");
-				}
-				if (my_image_no >= YSIZE(exp_metadata))
-				{
-					std::cerr<< "my_image_no= "<<my_image_no<<" YSIZE(exp_metadata)= "<< YSIZE(exp_metadata) <<std::endl;
-					REPORT_ERROR("my_image_no >= YSIZE(exp_metadata)");
-				}
+			if (part_id >= mydata.MDimg.numberOfObjects())
+			{
+				std::cerr<< "part_id= "<<part_id<<" mydata.MDimg.numberOfObjects()= "<< mydata.MDimg.numberOfObjects() <<std::endl;
+				REPORT_ERROR("part_id >= mydata.MDimg.numberOfObjects()");
+			}
+			if (my_image_no >= YSIZE(exp_metadata))
+			{
+				std::cerr<< "my_image_no= "<<my_image_no<<" YSIZE(exp_metadata)= "<< YSIZE(exp_metadata) <<std::endl;
+				REPORT_ERROR("my_image_no >= YSIZE(exp_metadata)");
+			}
 #endif
 
-				// Old optimal parameters
-				double old_rot, old_tilt, old_psi, old_xoff, old_yoff, old_zoff = 0.;
-				int old_iclass;
-				mydata.MDimg.getValue(EMDL_ORIENT_ROT,  old_rot, img_id);
-				mydata.MDimg.getValue(EMDL_ORIENT_TILT, old_tilt, img_id);
-				mydata.MDimg.getValue(EMDL_ORIENT_PSI,  old_psi, img_id);
-				mydata.MDimg.getValue(EMDL_ORIENT_ORIGIN_X, old_xoff, img_id);
-				mydata.MDimg.getValue(EMDL_ORIENT_ORIGIN_Y, old_yoff, img_id);
-				mydata.MDimg.getValue(EMDL_PARTICLE_CLASS, old_iclass, img_id);
-
-				// New optimal parameters
-				double rot = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ROT);
-				double tilt = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_TILT);
-				double psi = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PSI);
-				double xoff = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_XOFF);
-				double yoff = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_YOFF);
-				double zoff = 0.;
-				int iclass = (int)DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CLASS);
-
-				// Some orientational distance....
-				sum_changes_optimal_orientations += sampling.calculateAngularDistance(rot, tilt, psi, old_rot, old_tilt, old_psi);
-				sum_changes_optimal_offsets += (xoff-old_xoff)*(xoff-old_xoff) + (yoff-old_yoff)*(yoff-old_yoff) + (zoff-old_zoff)*(zoff-old_zoff);
-				if (iclass != old_iclass)
-					sum_changes_optimal_classes += 1.;
-				sum_changes_count += 1.;
-			} // end loop iseries
+			// Old optimal parameters
+			DOUBLE old_rot, old_tilt, old_psi, old_xoff, old_yoff, old_zoff = 0.;
+			int old_iclass;
+			mydata.MDimg.getValue(EMDL_ORIENT_ROT,  old_rot, part_id);
+			mydata.MDimg.getValue(EMDL_ORIENT_TILT, old_tilt, part_id);
+			mydata.MDimg.getValue(EMDL_ORIENT_PSI,  old_psi, part_id);
+			mydata.MDimg.getValue(EMDL_ORIENT_ORIGIN_X, old_xoff, part_id);
+			mydata.MDimg.getValue(EMDL_ORIENT_ORIGIN_Y, old_yoff, part_id);
+			if (mymodel.data_dim == 3)
+				mydata.MDimg.getValue(EMDL_ORIENT_ORIGIN_Z, old_zoff, part_id);
+			mydata.MDimg.getValue(EMDL_PARTICLE_CLASS, old_iclass, part_id);
+
+			// New optimal parameters
+			DOUBLE rot = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ROT);
+			DOUBLE tilt = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_TILT);
+			DOUBLE psi = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PSI);
+			DOUBLE xoff = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_XOFF);
+			DOUBLE yoff = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_YOFF);
+			DOUBLE zoff = 0.;
+			if (mymodel.data_dim == 3)
+				zoff = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ZOFF);
+			int iclass = (int)DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CLASS);
+
+			// Some orientational distance....
+			sum_changes_optimal_orientations += sampling.calculateAngularDistance(rot, tilt, psi, old_rot, old_tilt, old_psi);
+			sum_changes_optimal_offsets += (xoff-old_xoff)*(xoff-old_xoff) + (yoff-old_yoff)*(yoff-old_yoff) + (zoff-old_zoff)*(zoff-old_zoff);
+			if (iclass != old_iclass)
+				sum_changes_optimal_classes += 1.;
+			sum_changes_count += 1.;
 		} // end loop part_id (i)
 	} //end loop ori_part_id
 
@@ -5009,19 +5029,12 @@ void MlOptimiser::calculateExpectedAngularErrors(long int my_first_ori_particle,
 {
 
 	long int n_trials = 0;
-	exp_starting_image_no.clear();
-	exp_nr_images = 0;
-	for (long int ori_part_id = my_first_ori_particle, my_image_no = 0, ipart = 0; ori_part_id <= my_last_ori_particle; ori_part_id++)
+	for (long int ori_part_id = my_first_ori_particle; ori_part_id <= my_last_ori_particle; ori_part_id++)
     {
-		for (long int i = 0; i < mydata.ori_particles[ori_part_id].particles_id.size(); i++, ipart++)
-		{
-			long int part_id = mydata.ori_particles[ori_part_id].particles_id[i];
-			exp_starting_image_no.push_back(exp_nr_images);
-			exp_nr_images += mydata.getNrImagesInSeries(part_id);
-			n_trials++;
-		}
+		n_trials +=  mydata.ori_particles[ori_part_id].particles_id.size();
     }
 
+	int exp_current_image_size;
 	// Set exp_current_image_size to the coarse_size to calculate exepcted angular errors
 	if (strict_highres_exp > 0. && !do_acc_currentsize_despite_highres_exp)
 	{
@@ -5039,7 +5052,9 @@ void MlOptimiser::calculateExpectedAngularErrors(long int my_first_ori_particle,
 
 	// P(X | X_1) / P(X | X_2) = exp ( |F_1 - F_2|^2 / (-2 sigma2) )
 	// exp(-4.60517) = 0.01
-	double pvalue = 4.60517;
+	DOUBLE pvalue = 4.60517;
+	//if (mymodel.data_dim == 3)
+	//	pvalue *= 2.;
 
 	std::cout << " Estimating accuracies in the orientational assignment ... " << std::endl;
 	init_progress_bar(n_trials * mymodel.nr_classes);
@@ -5065,25 +5080,66 @@ void MlOptimiser::calculateExpectedAngularErrors(long int my_first_ori_particle,
 #endif
 		(mymodel.orientability_contrib)[iclass].initZeros(mymodel.ori_size/2 + 1);
 
-		double acc_rot_class = 0.;
-		double acc_trans_class = 0.;
+		DOUBLE acc_rot_class = 0.;
+		DOUBLE acc_trans_class = 0.;
 		// Particles are already in random order, so just move from 0 to n_trials
-		for (long int ori_part_id = my_first_ori_particle, my_image_no = 0, ipart = 0; ori_part_id <= my_last_ori_particle; ori_part_id++)
+		for (long int ori_part_id = my_first_ori_particle, my_metadata_entry = 0, ipart = 0; ori_part_id <= my_last_ori_particle; ori_part_id++)
 	    {
-			for (long int i = 0; i < mydata.ori_particles[ori_part_id].particles_id.size(); i++, ipart++)
+			for (long int ipart = 0; ipart < mydata.ori_particles[ori_part_id].particles_id.size(); ipart++, my_metadata_entry++)
 			{
-				long int part_id = mydata.ori_particles[ori_part_id].particles_id[i];
+				long int part_id = mydata.ori_particles[ori_part_id].particles_id[ipart];
+
+
+				MultidimArray<DOUBLE> Fctf;
+				// Get CTF for this particle
+				if (do_ctf_correction)
+				{
+					if (mymodel.data_dim == 3)
+					{
+						Image<DOUBLE> Ictf;
+						// Read CTF-image from disc
+						FileName fn_ctf;
+						std::istringstream split(exp_fn_ctf);
+						// Get the right line in the exp_fn_img string
+						for (int i = 0; i <= my_metadata_entry; i++)
+							getline(split, fn_ctf);
+						Ictf.read(fn_ctf);
+
+						// Set the CTF-image in Fctf
+						Ictf().setXmippOrigin();
+						Fctf.resize(exp_current_image_size, exp_current_image_size, exp_current_image_size/ 2 + 1);
+						FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(Fctf)
+						{
+							// Use negative kp, ip and jp indices, because the origin in the ctf_img lies half a pixel to the right of the actual center....
+							DIRECT_A3D_ELEM(Fctf, k, i, j) = A3D_ELEM(Ictf(), -kp, -ip, -jp);
+						}
+					}
+					else
+					{
+						CTF ctf;
+						ctf.setValues(DIRECT_A2D_ELEM(exp_metadata, my_metadata_entry, METADATA_CTF_DEFOCUS_U),
+									  DIRECT_A2D_ELEM(exp_metadata, my_metadata_entry, METADATA_CTF_DEFOCUS_V),
+									  DIRECT_A2D_ELEM(exp_metadata, my_metadata_entry, METADATA_CTF_DEFOCUS_ANGLE),
+									  DIRECT_A2D_ELEM(exp_metadata, my_metadata_entry, METADATA_CTF_VOLTAGE),
+									  DIRECT_A2D_ELEM(exp_metadata, my_metadata_entry, METADATA_CTF_CS),
+									  DIRECT_A2D_ELEM(exp_metadata, my_metadata_entry, METADATA_CTF_Q0),
+									  DIRECT_A2D_ELEM(exp_metadata, my_metadata_entry, METADATA_CTF_BFAC));
+
+						Fctf.resize(exp_current_image_size, exp_current_image_size/ 2 + 1);
+						ctf.getFftwImage(Fctf, mymodel.ori_size, mymodel.ori_size, mymodel.pixel_size, ctf_phase_flipped, only_flip_phases, intact_ctf_first_peak, true);
+					}
+				}
 
 				// Search 2 times: ang and off
 				// Don't estimate rotational accuracies if we're doing do_skip_rotate (for faster movie-frame alignment)
 				int imode_start = (do_skip_rotate) ? 1 : 0;
 				for (int imode = imode_start; imode < 2; imode++)
 				{
-					double ang_error = 0.;
-					double sh_error = 0.;
-					double ang_step;
-					double sh_step;
-					double my_snr = 0.;
+					DOUBLE ang_error = 0.;
+					DOUBLE sh_error = 0.;
+					DOUBLE ang_step;
+					DOUBLE sh_step;
+					DOUBLE my_snr = 0.;
 
 					// Search for ang_error and sh_error where there are at least 3-sigma differences!
 					// 13feb12: change for explicit probability at P=0.01
@@ -5127,152 +5183,184 @@ void MlOptimiser::calculateExpectedAngularErrors(long int my_first_ori_particle,
 
 						init_random_generator(random_seed + part_id);
 
-						// Loop over all images in the series
-						// TODO: check this for series!!
-						// Initialise the my_snr value (accumulate its sum for all images in the series!!)
-						my_snr = 0.;
-						for (int iseries = 0; iseries < mydata.getNrImagesInSeries(part_id); iseries++)
-						{
-
-							int my_image_no = exp_starting_image_no.at(ipart) + iseries;
-
-							Matrix2D<double> R_mic(3,3);
-							R_mic(0,0) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_0_0);
-							R_mic(0,1) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_0_1);
-							R_mic(0,2) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_0_2);
-							R_mic(1,0) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_1_0);
-							R_mic(1,1) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_1_1);
-							R_mic(1,2) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_1_2);
-							R_mic(2,0) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_2_0);
-							R_mic(2,1) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_2_1);
-							R_mic(2,2) = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_2_2);
-
-							int group_id = mydata.getGroupId(part_id, iseries);
+						int group_id = mydata.getGroupId(part_id);
 #ifdef DEBUG_CHECKSIZES
-							if (group_id  >= mymodel.sigma2_noise.size())
-							{
-								std::cerr<< "group_id = "<<group_id <<" mymodel.sigma2_noise.size()= "<< mymodel.sigma2_noise.size() <<std::endl;
-								REPORT_ERROR("group_id  >= mymodel.sigma2_noise.size()");
-							}
+						if (group_id  >= mymodel.sigma2_noise.size())
+						{
+							std::cerr<< "group_id = "<<group_id <<" mymodel.sigma2_noise.size()= "<< mymodel.sigma2_noise.size() <<std::endl;
+							REPORT_ERROR("group_id  >= mymodel.sigma2_noise.size()");
+						}
 #endif
-							MultidimArray<Complex > F1, F2;
-							MultidimArray<double> Fctf;
-							Matrix2D<double> A1, A2;
-
+						MultidimArray<Complex > F1, F2;
+						Matrix2D<DOUBLE> A1, A2;
 
-							// TODO: get values through exp_metadata?!
-							double rot1 = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ROT);
-							double tilt1 = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_TILT);
-							double psi1 = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PSI);
-							double xoff1 = 0.;
-							double yoff1 = 0.;
+						DOUBLE rot1 = DIRECT_A2D_ELEM(exp_metadata, my_metadata_entry, METADATA_ROT);
+						DOUBLE tilt1 = DIRECT_A2D_ELEM(exp_metadata, my_metadata_entry, METADATA_TILT);
+						DOUBLE psi1 = DIRECT_A2D_ELEM(exp_metadata, my_metadata_entry, METADATA_PSI);
+						DOUBLE xoff1 = 0.;
+						DOUBLE yoff1 = 0.;
+						DOUBLE zoff1 = 0.;
 
-							// Get the FT of the first image
+						if (mymodel.data_dim == 2)
 							F1.initZeros(exp_current_image_size, exp_current_image_size/ 2 + 1);
-
-							Euler_angles2matrix(rot1, tilt1, psi1, A1);
-							A1 = R_mic * A1.inv();
-							(mymodel.PPref[iclass]).get2DFourierTransform(F1, A1, IS_INV);
-
-							// Apply the angular or shift error
-							double rot2 = rot1;
-							double tilt2 = tilt1;
-							double psi2 = psi1;
-							Matrix1D<double> shift(2);
-							XX(shift) = xoff1;
-							YY(shift) = yoff1;
-							// Perturb psi or xoff , depending on the mode
-							if (imode == 0)
+						else
+							F1.initZeros(exp_current_image_size, exp_current_image_size, exp_current_image_size/ 2 + 1);
+
+						// Get the FT of the first image
+						Euler_angles2matrix(rot1, tilt1, psi1, A1);
+						(mymodel.PPref[iclass]).get2DFourierTransform(F1, A1, IS_NOT_INV);
+
+						// Apply the angular or shift error
+						DOUBLE rot2 = rot1;
+						DOUBLE tilt2 = tilt1;
+						DOUBLE psi2 = psi1;
+						DOUBLE xshift = xoff1;
+						DOUBLE yshift = yoff1;
+						DOUBLE zshift = zoff1;
+
+						// Perturb psi or xoff , depending on the mode
+						if (imode == 0)
+						{
+							if (mymodel.ref_dim == 3)
 							{
-								if (mymodel.ref_dim == 3)
-								{
-									// Randomly change rot, tilt or psi
-									double ran = rnd_unif();
-									if (ran < 0.3333)
-										rot2 = rot1 + ang_error;
-									else if (ran < 0.6667)
-										tilt2 = tilt1 + ang_error;
-									else
-										psi2  = psi1 + ang_error;
-								}
+								// Randomly change rot, tilt or psi
+								DOUBLE ran = rnd_unif();
+								if (ran < 0.3333)
+									rot2 = rot1 + ang_error;
+								else if (ran < 0.6667)
+									tilt2 = tilt1 + ang_error;
 								else
-								{
 									psi2  = psi1 + ang_error;
-								}
 							}
 							else
 							{
-								// Randomly change xoff or yoff
-								double ran = rnd_unif();
+								psi2  = psi1 + ang_error;
+							}
+						}
+						else
+						{
+							// Randomly change xoff or yoff
+							DOUBLE ran = rnd_unif();
+							if (mymodel.data_dim == 3)
+							{
+								if (ran < 0.3333)
+									xshift = xoff1 + sh_error;
+								else if (ran < 0.6667)
+									yshift = yoff1 + sh_error;
+								else
+									zshift = zoff1 + sh_error;
+							}
+							else
+							{
 								if (ran < 0.5)
-									XX(shift) = xoff1 + sh_error;
+									xshift = xoff1 + sh_error;
 								else
-									YY(shift) = yoff1 + sh_error;
+									yshift = yoff1 + sh_error;
 							}
-							// Get the FT of the second image
-							F2.initZeros(exp_current_image_size, exp_current_image_size / 2 + 1);
+						}
+						// Get the FT of the second image
+						if (mymodel.data_dim == 2)
+							F2.initZeros(exp_current_image_size, exp_current_image_size/ 2 + 1);
+						else
+							F2.initZeros(exp_current_image_size, exp_current_image_size, exp_current_image_size/ 2 + 1);
+
+						if (imode == 0)
+						{
+							// Get new rotated version of reference
 							Euler_angles2matrix(rot2, tilt2, psi2, A2);
-							A2 = R_mic * A2.inv();
-							(mymodel.PPref[iclass]).get2DFourierTransform(F2, A2, IS_INV);
-							if (ABS(XX(shift)) > 0. || ABS(YY(shift)) > 0.)
-								// shiftImageInFourierTransform takes shifts in pixels!
-								shiftImageInFourierTransform(F2, F2, (double) mymodel.ori_size, -shift);
-
-							// Apply CTF to F1 and F2 if necessary
-							if (do_ctf_correction)
-							{
-								CTF ctf;
-
-								ctf.setValues(DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_DEFOCUS_U),
-											  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_DEFOCUS_V),
-											  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_DEFOCUS_ANGLE),
-											  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_VOLTAGE),
-											  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_CS),
-											  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_Q0),
-											  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_BFAC));
-
-								Fctf.resize(F1);
-								ctf.getFftwImage(Fctf, mymodel.ori_size, mymodel.ori_size, mymodel.pixel_size, ctf_phase_flipped, only_flip_phases, intact_ctf_first_peak, true);
+							(mymodel.PPref[iclass]).get2DFourierTransform(F2, A2, IS_NOT_INV);
+						}
+						else
+						{
+							// Get shifted version
+							shiftImageInFourierTransform(F1, F2, (DOUBLE) mymodel.ori_size, -xshift, -yshift, -zshift);
+						}
+
+						// Apply CTF to F1 and F2 if necessary
+						if (do_ctf_correction)
+						{
 #ifdef DEBUG_CHECKSIZES
-								if (!Fctf.sameShape(F1) || !Fctf.sameShape(F2))
-								{
-									std::cerr<<" Fctf: "; Fctf.printShape(std::cerr);
-									std::cerr<<" F1:   "; F1.printShape(std::cerr);
-									std::cerr<<" F2:   "; F2.printShape(std::cerr);
-									REPORT_ERROR("ERROR: Fctf has a different shape from F1 and F2");
-								}
-#endif
-								FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(F1)
-								{
-									DIRECT_MULTIDIM_ELEM(F1, n) *= DIRECT_MULTIDIM_ELEM(Fctf, n);
-									DIRECT_MULTIDIM_ELEM(F2, n) *= DIRECT_MULTIDIM_ELEM(Fctf, n);
-								}
+							if (!Fctf.sameShape(F1) || !Fctf.sameShape(F2))
+							{
+								std::cerr<<" Fctf: "; Fctf.printShape(std::cerr);
+								std::cerr<<" F1:   "; F1.printShape(std::cerr);
+								std::cerr<<" F2:   "; F2.printShape(std::cerr);
+								REPORT_ERROR("ERROR: Fctf has a different shape from F1 and F2");
 							}
-
-							MultidimArray<int> * myMresol = (YSIZE(F1) == coarse_size) ? &Mresol_coarse : &Mresol_fine;
+#endif
 							FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(F1)
 							{
-								int ires = DIRECT_MULTIDIM_ELEM(*myMresol, n);
-								if (ires > 0)
-								{
-									my_snr += norm(DIRECT_MULTIDIM_ELEM(F1, n) - DIRECT_MULTIDIM_ELEM(F2, n)) / (2 * sigma2_fudge * mymodel.sigma2_noise[group_id](ires));
-								}
+								DIRECT_MULTIDIM_ELEM(F1, n) *= DIRECT_MULTIDIM_ELEM(Fctf, n);
+								DIRECT_MULTIDIM_ELEM(F2, n) *= DIRECT_MULTIDIM_ELEM(Fctf, n);
 							}
+						}
 
-							// Only for the psi-angle and the translations, and only when my_prob < 0.01 calculate a histogram of the contributions at each resolution shell
-							if (my_snr > pvalue && imode == 0)
+						MultidimArray<int> * myMresol = (YSIZE(F1) == coarse_size) ? &Mresol_coarse : &Mresol_fine;
+						my_snr = 0.;
+						FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(F1)
+						{
+							int ires = DIRECT_MULTIDIM_ELEM(*myMresol, n);
+							if (ires > 0)
 							{
-								FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(F1)
-								{
-									int ires = DIRECT_MULTIDIM_ELEM(*myMresol, n);
-									if (ires > 0)
-										mymodel.orientability_contrib[iclass](ires) +=
-												norm(DIRECT_MULTIDIM_ELEM(F1, n) - DIRECT_MULTIDIM_ELEM(F2, n)) / ( (2 * sigma2_fudge * mymodel.sigma2_noise[group_id](ires)) );
-								}
-
+								my_snr += norm(DIRECT_MULTIDIM_ELEM(F1, n) - DIRECT_MULTIDIM_ELEM(F2, n)) / (2 * sigma2_fudge * mymodel.sigma2_noise[group_id](ires) );
 							}
+						}
+//#define DEBUG_ANGACC
+#ifdef DEBUG_ANGACC
+							if (imode==0)
+							{
+								std::cerr << " ang_error= " << ang_error << std::endl;
+								std::cerr << " rot1= " << rot1 << " tilt1= " << tilt1 << " psi1= " << psi1 << std::endl;
+								std::cerr << " rot2= " << rot2 << " tilt2= " << tilt2 << " psi2= " << psi2 << std::endl;
 
-						} // end for iseries
+							}
+							else
+							{
+								std::cerr << " xshift= " << xshift << " yshift= " << yshift << " zshift= " << zshift << std::endl;
+								std::cerr << " sh_error= " << sh_error << std::endl;
+							}
+							std::cerr << " my_snr= " << my_snr << std::endl;
+							FourierTransformer transformer;
+							MultidimArray<DOUBLE> spec_img(mymodel.ori_size), spec_diff(mymodel.ori_size), count(mymodel.ori_size);
+							FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(F1)
+							{
+								long int idx = ROUND(sqrt(kp*kp + ip*ip + jp*jp));
+								spec_img(idx) += norm(dAkij(F1, k, i, j));
+								spec_diff(idx) += norm(dAkij(F1, k, i, j) - dAkij(F2, k, i, j));
+								count(idx) += 1.;
+							}
+							spec_img /= count;
+							spec_diff /= count;
+							for (int i=0; i < XSIZE(F1); i++)
+								std::cerr << " i= " << i << " spec_img(i)= "<< spec_img(i) << " spec_diff(i)= "<< spec_diff(i) << " sigma2_noise(i)= "<<  mymodel.sigma2_noise[group_id](i)
+								<< " count(i)= " << count(i) << " sum-diff-norm=" << count(i)*spec_diff(i)<< " sum-diff-norm/sigma2= " << count(i)*(spec_diff(i)/mymodel.sigma2_noise[group_id](i))<< std::endl;
+							Image<DOUBLE> tt;
+							if (mymodel.data_dim == 3)
+								tt().resize(YSIZE(F1), YSIZE(F1), YSIZE(F1));
+							else
+								tt().resize(YSIZE(F1), YSIZE(F1));
+							transformer.inverseFourierTransform(F1, tt());
+							CenterFFT(tt(),false);
+							tt.write("F1.spi");
+							transformer.inverseFourierTransform(F2, tt());
+							CenterFFT(tt(),false);
+							tt.write("F2.spi");
+							std::cerr << "Written F1.spi and F2.spi. Press any key to continue... "<<std::endl;
+							char c;
+							std::cin >> c;
+#endif
+
+						// Only for the psi-angle and the translations, and only when my_prob < 0.01 calculate a histogram of the contributions at each resolution shell
+						if (my_snr > pvalue && imode == 0)
+						{
+							FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(F1)
+							{
+								int ires = DIRECT_MULTIDIM_ELEM(*myMresol, n);
+								if (ires > 0)
+									mymodel.orientability_contrib[iclass](ires) +=
+											norm(DIRECT_MULTIDIM_ELEM(F1, n) - DIRECT_MULTIDIM_ELEM(F2, n)) / ( (2 * sigma2_fudge * mymodel.sigma2_noise[group_id](ires) ) );
+							}
+						}
 
 					} // end while my_snr >= pvalue
 					if (imode == 0)
@@ -5283,11 +5371,11 @@ void MlOptimiser::calculateExpectedAngularErrors(long int my_first_ori_particle,
 
 			}// end for part_id
 
-			progress_bar(n_trials*iclass + ipart);
+			progress_bar(n_trials*iclass + my_metadata_entry);
 		} // end for ori_part_id
 
-		mymodel.acc_rot[iclass]   = acc_rot_class / (double)n_trials;
-		mymodel.acc_trans[iclass] = acc_trans_class / (double)n_trials;
+		mymodel.acc_rot[iclass]   = acc_rot_class / (DOUBLE)n_trials;
+		mymodel.acc_trans[iclass] = acc_trans_class / (DOUBLE)n_trials;
 
 		// Store normalised spectral contributions to orientability
 		if (mymodel.orientability_contrib[iclass].sum() > 0.)
@@ -5299,7 +5387,7 @@ void MlOptimiser::calculateExpectedAngularErrors(long int my_first_ori_particle,
 
 
 		// Richard's formula with Greg's constant
-		//double b_orient = (acc_rot_class*acc_rot_class* particle_diameter*particle_diameter) / 3000.;
+		//DOUBLE b_orient = (acc_rot_class*acc_rot_class* particle_diameter*particle_diameter) / 3000.;
 		//std::cout << " + expected B-factor from the orientational errors = "
 		//		<< b_orient<<std::endl;
 		// B=8 PI^2 U^2
@@ -5312,7 +5400,7 @@ void MlOptimiser::calculateExpectedAngularErrors(long int my_first_ori_particle,
 
 	std::cout << " Auto-refine: Estimated accuracy angles= " << acc_rot<< " degrees; offsets= " << acc_trans << " pixels" << std::endl;
 	// Warn for inflated resolution estimates
-	if (acc_rot > 10.)
+	if (acc_rot > 10. && do_auto_refine)
 	{
 		std::cout << " Auto-refine: WARNING: The angular accuracy is worse than 10 degrees, so basically you cannot align your particles (yet)!" << std::endl;
 		std::cout << " Auto-refine: WARNING: You probably need not worry if the accuracy improves during the next few iterations." << std::endl;
@@ -5332,10 +5420,10 @@ void MlOptimiser::updateAngularSampling(bool verb)
 	{
 
 		// A. Adjust translational sampling to 75% of estimated accuracy
-		double new_step = XMIPP_MIN(1.5, 0.75 * acc_trans) * std::pow(2., adaptive_oversampling);
+		DOUBLE new_step = XMIPP_MIN(1.5, 0.75 * acc_trans) * std::pow(2., adaptive_oversampling);
 
 		// Search ranges are three times the estimates std.dev. in the offsets
-		double new_range = 3. * sqrt(mymodel.sigma2_offset);
+		DOUBLE new_range = 3. * sqrt(mymodel.sigma2_offset);
 
 		// Prevent too narrow searches: always at least 3x3 pixels in the coarse search
 		if (new_range < 1.5 * new_step)
@@ -5351,12 +5439,11 @@ void MlOptimiser::updateAngularSampling(bool verb)
 		if (!do_skip_rotate)
 		{
 			// B. Find the healpix order that corresponds to at least 50% of the estimated rotational accuracy
-			double angle_range = sqrt(mymodel.sigma2_rot) * 3.;
-			double new_ang_step, new_ang_step_wo_over;
+			DOUBLE angle_range = sqrt(mymodel.sigma2_rot) * 3.;
+			DOUBLE new_ang_step, new_ang_step_wo_over;
 			int new_hp_order;
 			for (new_hp_order = 0; new_hp_order < 8; new_hp_order++)
 			{
-
 				new_ang_step = 360. / (6 * ROUND(std::pow(2., new_hp_order + adaptive_oversampling)));
 				new_ang_step_wo_over = 2. * new_ang_step;
 				// Only consider healpix orders that gives at least more than one (non-oversampled) samplings within the local angular searches
@@ -5372,7 +5459,7 @@ void MlOptimiser::updateAngularSampling(bool verb)
 				// Set the new sampling in the sampling-object
 				sampling.setOrientations(new_hp_order, new_ang_step * std::pow(2., adaptive_oversampling));
 				// Resize the pdf_direction arrays to the correct size and fill with an even distribution
-				mymodel.initialisePdfDirection(sampling.NrDirections(0, true));
+				mymodel.initialisePdfDirection(sampling.NrDirections());
 				// Also reset the nr_directions in wsum_model
 				wsum_model.nr_directions = mymodel.nr_directions;
 				// Also resize and initialise wsum_model.pdf_direction for each class!
@@ -5385,11 +5472,11 @@ void MlOptimiser::updateAngularSampling(bool verb)
 	{
 
 		if (do_skip_rotate)
-			REPORT_ERROR("ERROR: --skip_rotate can only be used in classification or in movie-frame refinement ...");
+			REPORT_ERROR("ERROR: --skip_rotate can only be used in movie-frame refinement ...");
 
 		// Only change the sampling if the resolution has not improved during the last 2 iterations
 		// AND the hidden variables have not changed during the last 2 iterations
-		double old_rottilt_step = sampling.getAngularSampling(adaptive_oversampling);
+		DOUBLE old_rottilt_step = sampling.getAngularSampling(adaptive_oversampling);
 
 		// Only use a finer angular sampling is the angular accuracy is still above 75% of the estimated accuracy
 		// If it is already below, nothing will change and eventually nr_iter_wo_resol_gain or nr_iter_wo_large_hidden_variable_changes will go above MAX_NR_ITER_WO_RESOL_GAIN
@@ -5410,9 +5497,13 @@ void MlOptimiser::updateAngularSampling(bool verb)
 
 				// Prevent very coarse translational samplings: max 1.5
 				// Also stay a bit on the safe side with the translational sampling: 75% of estimated accuracy
-				double new_step = XMIPP_MIN(1.5, 0.75 * acc_trans) * std::pow(2., adaptive_oversampling);
+				DOUBLE new_step = XMIPP_MIN(1.5, 0.75 * acc_trans) * std::pow(2., adaptive_oversampling);
+                                // For subtomogram averaging: use at least half times previous step size
+                                if (mymodel.data_dim == 3) // TODO: check: this might just as well work for 2D data...
+                                    new_step = XMIPP_MAX(sampling.offset_step / 2., new_step);
 				// Search ranges are five times the last observed changes in offsets
-				double new_range = 5. * current_changes_optimal_offsets;
+                                // Only 3x for subtomogram averaging....
+				DOUBLE new_range = (mymodel.data_dim == 2) ? 5. * current_changes_optimal_offsets : 3 * current_changes_optimal_offsets;
 				// New range can only become 30% bigger than the previous range (to prevent very slow iterations in the beginning)
 				new_range = XMIPP_MIN(1.3*sampling.offset_range, new_range);
 				// Prevent too narrow searches: always at least 3x3 pixels in the coarse search
@@ -5429,7 +5520,7 @@ void MlOptimiser::updateAngularSampling(bool verb)
 
 				// B. Use twice as fine angular sampling
 				int new_hp_order;
-				double new_rottilt_step, new_psi_step;
+				DOUBLE new_rottilt_step, new_psi_step;
 				if (mymodel.ref_dim == 3)
 				{
 					new_hp_order = sampling.healpix_order + 1;
@@ -5447,7 +5538,7 @@ void MlOptimiser::updateAngularSampling(bool verb)
 				sampling.setOrientations(new_hp_order, new_psi_step * std::pow(2., adaptive_oversampling));
 
 				// Resize the pdf_direction arrays to the correct size and fill with an even distribution
-				mymodel.initialisePdfDirection(sampling.NrDirections(0, true));
+				mymodel.initialisePdfDirection(sampling.NrDirections());
 
 				// Also reset the nr_directions in wsum_model
 				wsum_model.nr_directions = mymodel.nr_directions;
@@ -5472,11 +5563,8 @@ void MlOptimiser::updateAngularSampling(bool verb)
 					mymodel.orientational_prior_mode = PRIOR_ROTTILT_PSI;
 					sampling.orientational_prior_mode = PRIOR_ROTTILT_PSI;
 					mymodel.sigma2_rot = mymodel.sigma2_tilt = mymodel.sigma2_psi = 2. * 2. * new_rottilt_step * new_rottilt_step;
-					nr_pool = max_nr_pool = 1;
 				}
-
 			}
-
 		}
 	}
 
@@ -5553,46 +5641,49 @@ void MlOptimiser::setMetaDataSubset(int first_ori_particle_id, int last_ori_part
 		}
 #endif
 
-		for (long int i = 0; i < mydata.ori_particles[ori_part_id].particles_id.size(); i++)
+		for (long int ipart = 0; ipart < mydata.ori_particles[ori_part_id].particles_id.size(); ipart++, my_image_no++)
 		{
-			long int part_id = mydata.ori_particles[ori_part_id].particles_id[i];
-
-			for (int iseries = 0; iseries < mydata.getNrImagesInSeries(part_id); iseries++, my_image_no++)
-			{
-
-				long int img_id = mydata.getImageId(part_id, iseries);
+			long int part_id = mydata.ori_particles[ori_part_id].particles_id[ipart];
 
 #ifdef DEBUG_CHECKSIZES
-				if (img_id >= mydata.MDimg.numberOfObjects())
-				{
-					std::cerr<< "img_id= "<<img_id<<" mydata.MDimg.numberOfObjects()= "<< mydata.MDimg.numberOfObjects() <<std::endl;
-					REPORT_ERROR("img_id >= mydata.MDimg.numberOfObjects()");
-				}
-				if (my_image_no >= YSIZE(exp_metadata))
-				{
-					std::cerr<< "my_image_no= "<<my_image_no<<" YSIZE(exp_metadata)= "<< YSIZE(exp_metadata) <<std::endl;
-					REPORT_ERROR("my_image_no >= YSIZE(exp_metadata)");
-				}
+			if (part_id >= mydata.MDimg.numberOfObjects())
+			{
+				std::cerr<< "part_id= "<<part_id<<" mydata.MDimg.numberOfObjects()= "<< mydata.MDimg.numberOfObjects() <<std::endl;
+				REPORT_ERROR("part_id >= mydata.MDimg.numberOfObjects()");
+			}
+			if (my_image_no >= YSIZE(exp_metadata))
+			{
+				std::cerr<< "my_image_no= "<<my_image_no<<" YSIZE(exp_metadata)= "<< YSIZE(exp_metadata) <<std::endl;
+				REPORT_ERROR("my_image_no >= YSIZE(exp_metadata)");
+			}
 #endif
-				mydata.MDimg.setValue(EMDL_ORIENT_ROT,  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ROT), img_id);
-				mydata.MDimg.setValue(EMDL_ORIENT_TILT, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_TILT), img_id);
-				mydata.MDimg.setValue(EMDL_ORIENT_PSI,  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PSI), img_id);
-				mydata.MDimg.setValue(EMDL_ORIENT_ORIGIN_X, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_XOFF), img_id);
-				mydata.MDimg.setValue(EMDL_ORIENT_ORIGIN_Y, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_YOFF), img_id);
-				mydata.MDimg.setValue(EMDL_PARTICLE_CLASS, (int)DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CLASS) , img_id);
-				mydata.MDimg.setValue(EMDL_PARTICLE_DLL,  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_DLL), img_id);
-				mydata.MDimg.setValue(EMDL_PARTICLE_PMAX, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PMAX), img_id);
-				mydata.MDimg.setValue(EMDL_PARTICLE_NR_SIGNIFICANT_SAMPLES,(int)DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NR_SIGN), img_id);
-				mydata.MDimg.setValue(EMDL_IMAGE_NORM_CORRECTION, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NORM), img_id);
-
-				// For the moment, CTF, prior and transformation matrix info is NOT updated...
-				double prior_x = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_XOFF_PRIOR);
-				double prior_y = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_YOFF_PRIOR);
-				if (prior_x < 999.)
-					mydata.MDimg.setValue(EMDL_ORIENT_ORIGIN_X_PRIOR, prior_x, img_id);
-				if (prior_y < 999.)
-					mydata.MDimg.setValue(EMDL_ORIENT_ORIGIN_Y_PRIOR, prior_y, img_id);
+			mydata.MDimg.setValue(EMDL_ORIENT_ROT,  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ROT), part_id);
+			mydata.MDimg.setValue(EMDL_ORIENT_TILT, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_TILT), part_id);
+			mydata.MDimg.setValue(EMDL_ORIENT_PSI,  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PSI), part_id);
+			mydata.MDimg.setValue(EMDL_ORIENT_ORIGIN_X, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_XOFF), part_id);
+			mydata.MDimg.setValue(EMDL_ORIENT_ORIGIN_Y, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_YOFF), part_id);
+			if (mymodel.data_dim == 3)
+				mydata.MDimg.setValue(EMDL_ORIENT_ORIGIN_Z, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ZOFF), part_id);
+			mydata.MDimg.setValue(EMDL_PARTICLE_CLASS, (int)DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CLASS) , part_id);
+			mydata.MDimg.setValue(EMDL_PARTICLE_DLL,  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_DLL), part_id);
+			mydata.MDimg.setValue(EMDL_PARTICLE_PMAX, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PMAX), part_id);
+			mydata.MDimg.setValue(EMDL_PARTICLE_NR_SIGNIFICANT_SAMPLES,(int)DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NR_SIGN), part_id);
+			mydata.MDimg.setValue(EMDL_IMAGE_NORM_CORRECTION, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NORM), part_id);
+
+			// For the moment, CTF, prior and transformation matrix info is NOT updated...
+			DOUBLE prior_x = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_XOFF_PRIOR);
+			DOUBLE prior_y = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_YOFF_PRIOR);
+			if (prior_x < 999.)
+				mydata.MDimg.setValue(EMDL_ORIENT_ORIGIN_X_PRIOR, prior_x, part_id);
+			if (prior_y < 999.)
+				mydata.MDimg.setValue(EMDL_ORIENT_ORIGIN_Y_PRIOR, prior_y, part_id);
+			if (mymodel.data_dim == 3)
+			{
+				DOUBLE prior_z = DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ZOFF_PRIOR);
+				if (prior_z < 999.)
+					mydata.MDimg.setValue(EMDL_ORIENT_ORIGIN_Z_PRIOR, prior_z, part_id);
 			}
+
 		}
 	}
 
@@ -5601,62 +5692,97 @@ void MlOptimiser::setMetaDataSubset(int first_ori_particle_id, int last_ori_part
 void MlOptimiser::getMetaAndImageDataSubset(int first_ori_particle_id, int last_ori_particle_id, bool do_also_imagedata)
 {
 
+	// Initialise filename strings if not reading imagedata here
+	if (!do_also_imagedata)
+	{
+		exp_fn_img = "";
+		exp_fn_ctf = "";
+		exp_fn_recimg = "";
+	}
+
 	int nr_images = 0;
 	for (long int ori_part_id = first_ori_particle_id; ori_part_id <= last_ori_particle_id; ori_part_id++)
 	{
+		nr_images += mydata.ori_particles[ori_part_id].particles_id.size();
+	}
 
-#ifdef DEBUG_CHECKSIZES
-		if (ori_part_id >= mydata.ori_particles.size())
+	exp_metadata.initZeros(nr_images, METADATA_LINE_LENGTH);
+	if (do_also_imagedata)
+	{
+		if (mymodel.data_dim == 3)
 		{
-			std::cerr<< "ori_part_id= "<<ori_part_id<<" mydata.ori_particles.size()= "<< mydata.ori_particles.size() <<std::endl;
-			REPORT_ERROR("ori_part_id >= mydata.ori_particles.size()");
-		}
-#endif
+			if (nr_images > 1)
+				REPORT_ERROR("MlOptimiser::getMetaAndImageDataSubset ERROR: cannot get multiple images for 3D data!");
 
-		for (long int i = 0; i < mydata.ori_particles[ori_part_id].particles_id.size(); i++)
+			if (do_ctf_correction)
+			{
+				if (has_converged && do_use_reconstruct_images)
+					exp_imagedata.resize(3*mymodel.ori_size, mymodel.ori_size, mymodel.ori_size);
+				else
+					exp_imagedata.resize(2*mymodel.ori_size, mymodel.ori_size, mymodel.ori_size);
+			}
+			else
+			{
+				if (has_converged && do_use_reconstruct_images)
+					exp_imagedata.resize(2*mymodel.ori_size, mymodel.ori_size, mymodel.ori_size);
+				else
+					exp_imagedata.resize(mymodel.ori_size, mymodel.ori_size, mymodel.ori_size);
+			}
+		}
+		else
 		{
-			long int part_id = mydata.ori_particles[ori_part_id].particles_id[i];
-			nr_images += mydata.getNrImagesInSeries(part_id);
+			if (has_converged && do_use_reconstruct_images)
+				exp_imagedata.resize(2*nr_images, mymodel.ori_size, mymodel.ori_size);
+			else
+				exp_imagedata.resize(nr_images, mymodel.ori_size, mymodel.ori_size);
 		}
 	}
 
-	exp_metadata.initZeros(nr_images, METADATA_LINE_LENGTH);
-	if (has_converged && do_use_reconstruct_images)
-		exp_imagedata.resize(2*nr_images, mymodel.ori_size, mymodel.ori_size);
-	else
-		exp_imagedata.resize(nr_images, mymodel.ori_size, mymodel.ori_size);
-
 	for (long int ori_part_id = first_ori_particle_id, my_image_no = 0; ori_part_id <= last_ori_particle_id; ori_part_id++)
     {
-		for (long int i = 0; i < mydata.ori_particles[ori_part_id].particles_id.size(); i++)
+		for (long int ipart = 0; ipart < mydata.ori_particles[ori_part_id].particles_id.size(); ipart++, my_image_no++)
 		{
-			long int part_id = mydata.ori_particles[ori_part_id].particles_id[i];
-			for (int iseries = 0; iseries < mydata.getNrImagesInSeries(part_id); iseries++, my_image_no++)
-			{
-				long int img_id = mydata.getImageId(part_id, iseries);
+			long int part_id = mydata.ori_particles[ori_part_id].particles_id[ipart];
 
 #ifdef DEBUG_CHECKSIZES
-				if (img_id >= mydata.MDimg.numberOfObjects())
-				{
-					std::cerr<< "img_id= "<<img_id<<" mydata.MDimg.numberOfObjects()= "<< mydata.MDimg.numberOfObjects() <<std::endl;
-					REPORT_ERROR("img_id >= mydata.MDimg.numberOfObjects()");
-				}
-				if (my_image_no >= YSIZE(exp_metadata))
-				{
-					std::cerr<< "my_image_no= "<<my_image_no<<" YSIZE(exp_metadata)= "<< YSIZE(exp_metadata) <<std::endl;
-					REPORT_ERROR("my_image_no >= YSIZE(exp_metadata)");
-				}
-				if (my_image_no >= nr_images)
-				{
-					std::cerr<< "my_image_no= "<<my_image_no<<" nr_images= "<< nr_images <<std::endl;
-					REPORT_ERROR("my_image_no >= nr_images");
-				}
+			if (part_id >= mydata.MDimg.numberOfObjects())
+			{
+				std::cerr<< "part_id= "<<part_id<<" mydata.MDimg.numberOfObjects()= "<< mydata.MDimg.numberOfObjects() <<std::endl;
+				REPORT_ERROR("part_id >= mydata.MDimg.numberOfObjects()");
+			}
+			if (my_image_no >= YSIZE(exp_metadata))
+			{
+				std::cerr<< "my_image_no= "<<my_image_no<<" YSIZE(exp_metadata)= "<< YSIZE(exp_metadata) <<std::endl;
+				REPORT_ERROR("my_image_no >= YSIZE(exp_metadata)");
+			}
+			if (my_image_no >= nr_images)
+			{
+				std::cerr<< "my_image_no= "<<my_image_no<<" nr_images= "<< nr_images <<std::endl;
+				REPORT_ERROR("my_image_no >= nr_images");
+			}
 #endif
+			// Get the image names from the MDimg table
+			FileName fn_img="", fn_rec_img="", fn_ctf="";
+			mydata.MDimg.getValue(EMDL_IMAGE_NAME, fn_img, part_id);
+			if (mymodel.data_dim == 3 && do_ctf_correction)
+			{
+				// Also read the CTF image from disc
+				if (!mydata.MDimg.getValue(EMDL_CTF_IMAGE, fn_ctf, part_id))
+					REPORT_ERROR("MlOptimiser::getMetaAndImageDataSubset ERROR: cannot find rlnCtfImage for 3D CTF correction!");
+			}
+			if (has_converged && do_use_reconstruct_images)
+			{
+				mydata.MDimg.getValue(EMDL_IMAGE_RECONSTRUCT_NAME, fn_rec_img, part_id);
+			}
+
+			if (do_also_imagedata)
+			{
 				// First read the image from disc
-				FileName fn_img, fn_rec_img;
-				mydata.MDimg.getValue(EMDL_IMAGE_NAME, fn_img, img_id);
-				Image<double> img, rec_img;
-				img.read(fn_img);
+				Image<DOUBLE> img, rec_img;
+				if (do_preread_images)
+					img() = mydata.particles[part_id].img;
+				else
+					img.read(fn_img);
 				if (XSIZE(img()) != XSIZE(exp_imagedata) || YSIZE(img()) != YSIZE(exp_imagedata) )
 				{
 					std::cerr << " fn_img= " << fn_img << " XSIZE(img())= " << XSIZE(img()) << " YSIZE(img())= " << YSIZE(img()) << std::endl;
@@ -5664,7 +5790,6 @@ void MlOptimiser::getMetaAndImageDataSubset(int first_ori_particle_id, int last_
 				}
 				if (has_converged && do_use_reconstruct_images)
 				{
-					mydata.MDimg.getValue(EMDL_IMAGE_RECONSTRUCT_NAME, fn_rec_img, img_id);
 					rec_img.read(fn_rec_img);
 					if (XSIZE(rec_img()) != XSIZE(exp_imagedata) || YSIZE(rec_img()) != YSIZE(exp_imagedata) )
 					{
@@ -5672,111 +5797,142 @@ void MlOptimiser::getMetaAndImageDataSubset(int first_ori_particle_id, int last_
 						REPORT_ERROR("MlOptimiser::getMetaAndImageDataSubset ERROR: incorrect reconstruct_image size");
 					}
 				}
-				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY2D(img())
+				if (mymodel.data_dim == 3)
 				{
-					DIRECT_A3D_ELEM(exp_imagedata, my_image_no, i, j) = DIRECT_A2D_ELEM(img(), i, j);
-				}
 
-				if (has_converged && do_use_reconstruct_images)
-				{
-					FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY2D(rec_img())
+					FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(img())
 					{
-						DIRECT_A3D_ELEM(exp_imagedata, nr_images + my_image_no, i, j) = DIRECT_A2D_ELEM(rec_img(), i, j);
+						DIRECT_A3D_ELEM(exp_imagedata, k, i, j) = DIRECT_A3D_ELEM(img(), k, i, j);
 					}
-				}
 
-				// Now get the metadata
-				int iaux;
-				mydata.MDimg.getValue(EMDL_ORIENT_ROT,  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ROT), img_id);
-				mydata.MDimg.getValue(EMDL_ORIENT_TILT, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_TILT), img_id);
-				mydata.MDimg.getValue(EMDL_ORIENT_PSI,  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PSI), img_id);
-				mydata.MDimg.getValue(EMDL_ORIENT_ORIGIN_X, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_XOFF), img_id);
-				mydata.MDimg.getValue(EMDL_ORIENT_ORIGIN_Y, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_YOFF), img_id);
-				mydata.MDimg.getValue(EMDL_PARTICLE_CLASS, iaux, img_id);
-				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CLASS) = (double)iaux;
-				mydata.MDimg.getValue(EMDL_PARTICLE_DLL,  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_DLL), img_id);
-				mydata.MDimg.getValue(EMDL_PARTICLE_PMAX, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PMAX), img_id);
-				mydata.MDimg.getValue(EMDL_PARTICLE_NR_SIGNIFICANT_SAMPLES, iaux, img_id);
-				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NR_SIGN) = (double)iaux;
-				if (!mydata.MDimg.getValue(EMDL_IMAGE_NORM_CORRECTION, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NORM), img_id))
-					DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NORM) = 1.;
-				if (do_ctf_correction)
+					if (do_ctf_correction)
+					{
+						img.read(fn_ctf);
+						FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(img())
+						{
+							DIRECT_A3D_ELEM(exp_imagedata, mymodel.ori_size + k, i, j) = DIRECT_A3D_ELEM(img(), k, i, j);
+						}
+					}
+
+					if (has_converged && do_use_reconstruct_images)
+					{
+						int offset = (do_ctf_correction) ? 2 * mymodel.ori_size : mymodel.ori_size;
+						FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(img())
+						{
+							DIRECT_A3D_ELEM(exp_imagedata, offset + k, i, j) = DIRECT_A3D_ELEM(rec_img(), k, i, j);
+						}
+					}
+
+				}
+				else
 				{
-					long int mic_id = mydata.getMicrographId(part_id, iseries);
-					double kV, DeltafU, DeltafV, azimuthal_angle, Cs, Bfac, Q0;
-					if (!mydata.MDimg.getValue(EMDL_CTF_VOLTAGE, kV, img_id))
-						if (!mydata.MDmic.getValue(EMDL_CTF_VOLTAGE, kV, mic_id))
-							kV=200;
-
-					if (!mydata.MDimg.getValue(EMDL_CTF_DEFOCUSU, DeltafU, img_id))
-						if (!mydata.MDmic.getValue(EMDL_CTF_DEFOCUSU, DeltafU, mic_id))
-							DeltafU=0;
-
-					if (!mydata.MDimg.getValue(EMDL_CTF_DEFOCUSV, DeltafV, img_id))
-						if (!mydata.MDmic.getValue(EMDL_CTF_DEFOCUSV, DeltafV, mic_id))
-							DeltafV=DeltafU;
-
-					if (!mydata.MDimg.getValue(EMDL_CTF_DEFOCUS_ANGLE, azimuthal_angle, img_id))
-						if (!mydata.MDmic.getValue(EMDL_CTF_DEFOCUS_ANGLE, azimuthal_angle, mic_id))
-							azimuthal_angle=0;
-
-					if (!mydata.MDimg.getValue(EMDL_CTF_CS, Cs, img_id))
-						if (!mydata.MDmic.getValue(EMDL_CTF_CS, Cs, mic_id))
-							Cs=0;
-
-					if (!mydata.MDimg.getValue(EMDL_CTF_BFACTOR, Bfac, img_id))
-						if (!mydata.MDmic.getValue(EMDL_CTF_BFACTOR, Bfac, mic_id))
-							Bfac=0;
-
-					if (!mydata.MDimg.getValue(EMDL_CTF_Q0, Q0, img_id))
-						if (!mydata.MDmic.getValue(EMDL_CTF_Q0, Q0, mic_id))
-							Q0=0;
-
-					DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_VOLTAGE) = kV;
-					DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_DEFOCUS_U) = DeltafU;
-					DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_DEFOCUS_V) = DeltafV;
-					DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_DEFOCUS_ANGLE) = azimuthal_angle;
-					DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_CS) = Cs;
-					DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_BFAC) = Bfac;
-					DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_Q0) = Q0;
+					FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY2D(img())
+					{
+						DIRECT_A3D_ELEM(exp_imagedata, my_image_no, i, j) = DIRECT_A2D_ELEM(img(), i, j);
+					}
+
+					if (has_converged && do_use_reconstruct_images)
+					{
+						FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY2D(rec_img())
+						{
+							DIRECT_A3D_ELEM(exp_imagedata, nr_images + my_image_no, i, j) = DIRECT_A2D_ELEM(rec_img(), i, j);
+						}
+					}
 
 				}
+			}
+			else
+			{
+				exp_fn_img += fn_img + "\n";
+				if (fn_ctf != "")
+					exp_fn_ctf += fn_ctf + "\n";
+				if (fn_rec_img != "")
+					exp_fn_recimg += fn_rec_img + "\n";
+			}
 
-				// beamtilt
-				double beamtilt_x = 0., beamtilt_y = 0.;
-				if (mydata.MDimg.containsLabel(EMDL_IMAGE_BEAMTILT_X))
-					mydata.MDimg.getValue(EMDL_IMAGE_BEAMTILT_X, beamtilt_x, img_id);
-				if (mydata.MDimg.containsLabel(EMDL_IMAGE_BEAMTILT_Y))
-					mydata.MDimg.getValue(EMDL_IMAGE_BEAMTILT_Y, beamtilt_y, img_id);
-				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_BEAMTILT_X) = beamtilt_x;
-				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_BEAMTILT_Y) = beamtilt_y;
-
-				// If the priors are NOT set, then set their values to 999.
-				if (!mydata.MDimg.getValue(EMDL_ORIENT_ROT_PRIOR,  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ROT_PRIOR), img_id))
-					DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ROT_PRIOR) = 999.;
-				if (!mydata.MDimg.getValue(EMDL_ORIENT_TILT_PRIOR, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_TILT_PRIOR), img_id))
-					DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_TILT_PRIOR) = 999.;
-				if (!mydata.MDimg.getValue(EMDL_ORIENT_PSI_PRIOR,  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PSI_PRIOR), img_id))
-					DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PSI_PRIOR) = 999.;
-				if (!mydata.MDimg.getValue(EMDL_ORIENT_ORIGIN_X_PRIOR, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_XOFF_PRIOR), img_id))
-					DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_XOFF_PRIOR) = 999.;
-				if (!mydata.MDimg.getValue(EMDL_ORIENT_ORIGIN_Y_PRIOR, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_YOFF_PRIOR), img_id))
-					DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_YOFF_PRIOR) = 999.;
-
-				// Pass the transformation matrix (even if it is the Identity matrix...
-				Matrix2D<double> R_mic;
-				R_mic = mydata.getMicrographTransformationMatrix(part_id, iseries);
-				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_0_0) = R_mic(0,0);
-				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_0_1) = R_mic(0,1);
-				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_0_2) = R_mic(0,2);
-				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_1_0) = R_mic(1,0);
-				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_1_1) = R_mic(1,1);
-				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_1_2) = R_mic(1,2);
-				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_2_0) = R_mic(2,0);
-				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_2_1) = R_mic(2,1);
-				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_MAT_2_2) = R_mic(2,2);
+			// Now get the metadata
+			int iaux;
+			mydata.MDimg.getValue(EMDL_ORIENT_ROT,  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ROT), part_id);
+			mydata.MDimg.getValue(EMDL_ORIENT_TILT, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_TILT), part_id);
+			mydata.MDimg.getValue(EMDL_ORIENT_PSI,  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PSI), part_id);
+			mydata.MDimg.getValue(EMDL_ORIENT_ORIGIN_X, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_XOFF), part_id);
+			mydata.MDimg.getValue(EMDL_ORIENT_ORIGIN_Y, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_YOFF), part_id);
+			if (mymodel.data_dim == 3)
+				mydata.MDimg.getValue(EMDL_ORIENT_ORIGIN_Z, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ZOFF), part_id);
+			mydata.MDimg.getValue(EMDL_PARTICLE_CLASS, iaux, part_id);
+			DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CLASS) = (DOUBLE)iaux;
+			mydata.MDimg.getValue(EMDL_PARTICLE_DLL,  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_DLL), part_id);
+			mydata.MDimg.getValue(EMDL_PARTICLE_PMAX, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PMAX), part_id);
+			mydata.MDimg.getValue(EMDL_PARTICLE_NR_SIGNIFICANT_SAMPLES, iaux, part_id);
+			DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NR_SIGN) = (DOUBLE)iaux;
+			if (!mydata.MDimg.getValue(EMDL_IMAGE_NORM_CORRECTION, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NORM), part_id))
+				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_NORM) = 1.;
+			if (do_ctf_correction)
+			{
+				long int mic_id = mydata.getMicrographId(part_id);
+				DOUBLE kV, DeltafU, DeltafV, azimuthal_angle, Cs, Bfac, Q0;
+				if (!mydata.MDimg.getValue(EMDL_CTF_VOLTAGE, kV, part_id))
+					if (!mydata.MDmic.getValue(EMDL_CTF_VOLTAGE, kV, mic_id))
+						kV=200;
+
+				if (!mydata.MDimg.getValue(EMDL_CTF_DEFOCUSU, DeltafU, part_id))
+					if (!mydata.MDmic.getValue(EMDL_CTF_DEFOCUSU, DeltafU, mic_id))
+						DeltafU=0;
+
+				if (!mydata.MDimg.getValue(EMDL_CTF_DEFOCUSV, DeltafV, part_id))
+					if (!mydata.MDmic.getValue(EMDL_CTF_DEFOCUSV, DeltafV, mic_id))
+						DeltafV=DeltafU;
+
+				if (!mydata.MDimg.getValue(EMDL_CTF_DEFOCUS_ANGLE, azimuthal_angle, part_id))
+					if (!mydata.MDmic.getValue(EMDL_CTF_DEFOCUS_ANGLE, azimuthal_angle, mic_id))
+						azimuthal_angle=0;
+
+				if (!mydata.MDimg.getValue(EMDL_CTF_CS, Cs, part_id))
+					if (!mydata.MDmic.getValue(EMDL_CTF_CS, Cs, mic_id))
+						Cs=0;
+
+				if (!mydata.MDimg.getValue(EMDL_CTF_BFACTOR, Bfac, part_id))
+					if (!mydata.MDmic.getValue(EMDL_CTF_BFACTOR, Bfac, mic_id))
+						Bfac=0;
+
+				if (!mydata.MDimg.getValue(EMDL_CTF_Q0, Q0, part_id))
+					if (!mydata.MDmic.getValue(EMDL_CTF_Q0, Q0, mic_id))
+						Q0=0;
+
+				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_VOLTAGE) = kV;
+				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_DEFOCUS_U) = DeltafU;
+				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_DEFOCUS_V) = DeltafV;
+				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_DEFOCUS_ANGLE) = azimuthal_angle;
+				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_CS) = Cs;
+				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_BFAC) = Bfac;
+				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_CTF_Q0) = Q0;
 
 			}
+
+			// beamtilt
+			DOUBLE beamtilt_x = 0., beamtilt_y = 0.;
+			if (mydata.MDimg.containsLabel(EMDL_IMAGE_BEAMTILT_X))
+				mydata.MDimg.getValue(EMDL_IMAGE_BEAMTILT_X, beamtilt_x, part_id);
+			if (mydata.MDimg.containsLabel(EMDL_IMAGE_BEAMTILT_Y))
+				mydata.MDimg.getValue(EMDL_IMAGE_BEAMTILT_Y, beamtilt_y, part_id);
+			DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_BEAMTILT_X) = beamtilt_x;
+			DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_BEAMTILT_Y) = beamtilt_y;
+
+			// If the priors are NOT set, then set their values to 999.
+			if (!mydata.MDimg.getValue(EMDL_ORIENT_ROT_PRIOR,  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ROT_PRIOR), part_id))
+				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ROT_PRIOR) = 999.;
+			if (!mydata.MDimg.getValue(EMDL_ORIENT_TILT_PRIOR, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_TILT_PRIOR), part_id))
+				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_TILT_PRIOR) = 999.;
+			if (!mydata.MDimg.getValue(EMDL_ORIENT_PSI_PRIOR,  DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PSI_PRIOR), part_id))
+				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_PSI_PRIOR) = 999.;
+			if (!mydata.MDimg.getValue(EMDL_ORIENT_ORIGIN_X_PRIOR, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_XOFF_PRIOR), part_id))
+				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_XOFF_PRIOR) = 999.;
+			if (!mydata.MDimg.getValue(EMDL_ORIENT_ORIGIN_Y_PRIOR, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_YOFF_PRIOR), part_id))
+				DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_YOFF_PRIOR) = 999.;
+			if (mymodel.data_dim == 3)
+				if (!mydata.MDimg.getValue(EMDL_ORIENT_ORIGIN_Z_PRIOR, DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ZOFF_PRIOR), part_id))
+					DIRECT_A2D_ELEM(exp_metadata, my_image_no, METADATA_ZOFF_PRIOR) = 999.;
+
 		}
     }
 
diff --git a/src/ml_optimiser.h b/src/ml_optimiser.h
index af6e62a..187e200 100644
--- a/src/ml_optimiser.h
+++ b/src/ml_optimiser.h
@@ -21,6 +21,7 @@
 #ifndef ML_OPTIMISER_H_
 #define ML_OPTIMISER_H_
 
+#include <pthread.h>
 #include "src/ml_model.h"
 #include "src/parallel.h"
 #include "src/exp_model.h"
@@ -67,16 +68,6 @@
 #define METADATA_BEAMTILT_X 24
 #define METADATA_BEAMTILT_Y 25
 
-#define METADATA_MAT_0_0 26
-#define METADATA_MAT_0_1 27
-#define METADATA_MAT_0_2 28
-#define METADATA_MAT_1_0 29
-#define METADATA_MAT_1_1 30
-#define METADATA_MAT_1_2 31
-#define METADATA_MAT_2_0 32
-#define METADATA_MAT_2_1 33
-#define METADATA_MAT_2_2 34
-
 #define DO_WRITE_DATA true
 #define DONT_WRITE_DATA false
 #define DO_WRITE_SAMPLING true
@@ -130,7 +121,7 @@ public:
 	bool fix_tau;
 
     // some parameters for debugging
-	double debug1, debug2;
+	DOUBLE debug1, debug2;
 
 	// Starting and finishing particles (for parallelisation)
     long int my_first_ori_particle_id, my_last_ori_particle_id;
@@ -142,7 +133,7 @@ public:
 	bool do_split_random_halves;
 
 	// resolution (in Angstrom) to join the two random halves
-	double low_resol_join_halves;
+	DOUBLE low_resol_join_halves;
 
 	// Flag to join random halves again
 	bool do_join_random_halves;
@@ -184,7 +175,7 @@ public:
 	int nr_iter_wo_resol_gain;
 
 	// Best resolution obtained thus far
-	double best_resol_thus_far;
+	DOUBLE best_resol_thus_far;
 
 	// Is the FSC still high at the resolution limit?
 	bool has_high_fsc_at_limit;
@@ -196,22 +187,22 @@ public:
 	int autosampling_hporder_local_searches;
 
 	// Smallest changes thus far in the optimal translational offsets, orientations and classes
-	double smallest_changes_optimal_offsets;
-	double smallest_changes_optimal_orientations;
+	DOUBLE smallest_changes_optimal_offsets;
+	DOUBLE smallest_changes_optimal_orientations;
 	int smallest_changes_optimal_classes;
 
 	// Number of iterations without a decrease in OffsetChanges
 	int nr_iter_wo_large_hidden_variable_changes;
 
 	// Strict high-res limit in the expectation step
-	double strict_highres_exp;
+	DOUBLE strict_highres_exp;
 
 	// Flag to indicate to estimate angular accuracy until current_size (and not coarse_size) when restricting high-res limit in the expectation step
 	// This is used for testing purposes only
 	bool do_acc_currentsize_despite_highres_exp;
 
 	// Global parameters to store accuracy on rot and trans
-	double acc_rot, acc_trans;
+	DOUBLE acc_rot, acc_trans;
 
 	// Flag to indicate to use all data out to Nyquist
 	bool do_use_all_data;
@@ -235,7 +226,7 @@ public:
 	int max_coarse_size;
 
 	// Particle diameter (in Ang)
-	double particle_diameter;
+	DOUBLE particle_diameter;
 
 	// How many fourier shells should be included beyond the highest shell where evidenceVsPriorRatio < 1?
 	int incr_size;
@@ -257,10 +248,10 @@ public:
 	int width_mask_edge;
 
 	// Number of particles to be processed simultaneously
-	int nr_pool, max_nr_pool;
+	int nr_pool;
 
 	// Available memory (in Gigabyte)
-	double available_memory;
+	DOUBLE available_memory;
 
 	// Perform combination of weight through files written on disc
 	bool combine_weights_thru_disc;
@@ -268,6 +259,12 @@ public:
 	// Only print metadata label definitions and exit
 	bool do_print_metadata_labels;
 
+        // Use parallel access to disc?
+        bool do_parallel_disc_io;
+
+        // Or preread all images into RAM on the master node?
+        bool do_preread_images;
+
 	// Print the symmetry transformation matrices
 	bool do_print_symmetry_ops;
 
@@ -290,7 +287,7 @@ public:
 	 * The closer to one, the more orientations will be oversampled
 	 * The default is 0.999.
 	 */
-	double adaptive_fraction;
+	DOUBLE adaptive_fraction;
 
 	// Seed for random number generator
 	int random_seed;
@@ -313,7 +310,7 @@ public:
 	bool do_always_cc;
 
 	// Initial low-pass filter for all references (in digital frequency)
-	double ini_high;
+	DOUBLE ini_high;
 
 	// Flag whether to generate seeds
 	// TODO: implement!
@@ -358,7 +355,7 @@ public:
 	FileName fn_sigma;
 
 	// Multiplicative fdge factor for the sigma estimates
-	double sigma2_fudge;
+	DOUBLE sigma2_fudge;
 
 	// Perform two reconstructions of random halves sequentially (to save memory for very big cases)
 	bool do_sequential_halves_recons;
@@ -368,24 +365,19 @@ public:
 	// Until now the best refinements have used the noisy mask, not the soft mask....
 	bool do_zero_mask;
 
-
-	// Developmental: simulated annealing to get out of local minima...
-	bool do_sim_anneal;
-	double temperature, temp_ini, temp_fin;
-
 	/////////// Keep track of hidden variable changes ////////////////////////
 
 	// Changes from one iteration to the next in the angles
-	double current_changes_optimal_orientations, sum_changes_optimal_orientations;
+	DOUBLE current_changes_optimal_orientations, sum_changes_optimal_orientations;
 
 	// Changes from one iteration to the next in the translations
-	double current_changes_optimal_offsets, sum_changes_optimal_offsets;
+	DOUBLE current_changes_optimal_offsets, sum_changes_optimal_offsets;
 
 	// Changes from one iteration to the next in the class assignments
-	double current_changes_optimal_classes, sum_changes_optimal_classes;
+	DOUBLE current_changes_optimal_classes, sum_changes_optimal_classes;
 
 	// Just count how often the optimal changes are summed
-	double sum_changes_count;
+	DOUBLE sum_changes_count;
 
 	/////////// Some internal stuff ////////////////////////
 
@@ -396,49 +388,59 @@ public:
 	TabSine tab_sin;
 	TabCosine tab_cos;
 
-	// Loop from iclass_min to iclass_max to deal with seed generation in the first iteration
-	int iclass_min, iclass_max;
-
 	// Verbosity flag
 	int verb;
 
-	// Thread Managers for the expectation step: one for allorientations, the other for all (pooled) particles
-	ThreadTaskDistributor *exp_iorient_ThreadTaskDistributor, *exp_ipart_ThreadTaskDistributor;
+	// Thread Managers for the expectation step: one for all (pooled) particles
+	ThreadTaskDistributor *exp_ipart_ThreadTaskDistributor;
 
 	// Number of threads to run in parallel
-	int nr_threads, nr_threads_original;
+	int nr_threads;
+
+	long int exp_my_first_ori_particle, exp_my_last_ori_particle;
+	MultidimArray<DOUBLE> exp_metadata, exp_imagedata;
+	std::string exp_fn_img, exp_fn_ctf, exp_fn_recimg;
+	int exp_nr_images;
+
+	// Calculate translated images on-the-fly
+	bool do_shifts_onthefly;
+	std::vector<MultidimArray<Complex> > global_fftshifts_ab_coarse, global_fftshifts_ab_current, global_fftshifts_ab2_coarse, global_fftshifts_ab2_current;
 
 	/** Some global variables that are only for thread visibility */
 	/// Taken from getAllSquaredDifferences
+	/*
 	std::vector<MultidimArray<Complex > > exp_Fimgs, exp_Fimgs_nomask, exp_local_Fimgs_shifted, exp_local_Fimgs_shifted_nomask;
-	std::vector<MultidimArray<double> > exp_Fctfs, exp_local_Fctfs, exp_local_Minvsigma2s;
-	Matrix2D<double> exp_R_mic;
+	std::vector<MultidimArray<DOUBLE> > exp_Fctfs, exp_local_Fctfs, exp_local_Minvsigma2s;
+	Matrix2D<DOUBLE> exp_R_mic;
 	int exp_iseries, exp_iclass, exp_ipass, exp_iimage, exp_ipart, exp_current_image_size, exp_current_oversampling, exp_nr_ori_particles, exp_nr_particles, exp_nr_images;
 	long int exp_nr_oversampled_rot, exp_nr_oversampled_trans, exp_nr_rot, exp_nr_dir, exp_nr_psi, exp_nr_trans;
-	long int exp_part_id, exp_my_first_ori_particle, exp_my_last_ori_particle;
+	long int exp_part_id, exp_my_first_ori_particle, exp_my_last_ori_particle
 	std::vector<int> exp_starting_image_no;
 	std::vector<long int> exp_ipart_to_part_id, exp_ipart_to_ori_part_id, exp_ipart_to_ori_part_nframe, exp_iimg_to_ipart;
-	std::vector<double> exp_highres_Xi2_imgs, exp_min_diff2, exp_local_sqrtXi2, exp_local_oldcc;
-	MultidimArray<double> exp_Mweight;
+	std::vector<DOUBLE> exp_highres_Xi2_imgs, exp_min_diff2, exp_local_sqrtXi2, exp_local_oldcc;
+	MultidimArray<DOUBLE> exp_Mweight;
 	MultidimArray<bool> exp_Mcoarse_significant;
 	// And from storeWeightedSums
-	std::vector<double> exp_sum_weight, exp_significant_weight, exp_max_weight;
-	std::vector<Matrix1D<double> > exp_old_offset, exp_prior;
-	std::vector<double> exp_wsum_norm_correction;
-	std::vector<MultidimArray<double> > exp_wsum_scale_correction_XA, exp_wsum_scale_correction_AA, exp_power_imgs;
-	MultidimArray<double> exp_metadata, exp_imagedata;
-	double exp_thisparticle_sumweight;
-
+	std::vector<DOUBLE> exp_sum_weight, exp_significant_weight, exp_max_weight;
+	std::vector<Matrix1D<DOUBLE> > exp_old_offset, exp_prior;
+	std::vector<DOUBLE> exp_wsum_norm_correction;
+	std::vector<MultidimArray<DOUBLE> > exp_wsum_scale_correction_XA, exp_wsum_scale_correction_AA, exp_power_imgs;
+	DOUBLE exp_thisparticle_sumweight;
+	*/
 	//TMP DEBUGGING
-	MultidimArray<double> DEBUGGING_COPY_exp_Mweight;
+	MultidimArray<DOUBLE> DEBUGGING_COPY_exp_Mweight;
 
 #ifdef TIMING
     Timer timer;
 	int TIMING_DIFF_PROJ, TIMING_DIFF_SHIFT, TIMING_DIFF_DIFF2;
 	int TIMING_WSUM_PROJ, TIMING_WSUM_BACKPROJ, TIMING_WSUM_DIFF2, TIMING_WSUM_SUMSHIFT;
 	int TIMING_EXP, TIMING_MAX, TIMING_RECONS;
-	int TIMING_ESP, TIMING_ESP_READ, TIMING_ESP_DIFF1, TIMING_ESP_DIFF2;
+	int TIMING_ESP, TIMING_ESP_THR, TIMING_ESP_ONEPART, TIMING_ESP_ONEPARTN, TIMING_EXP_METADATA, TIMING_EXP_CHANGES;
+	int TIMING_ESP_FT, TIMING_ESP_INI, TIMING_ESP_DIFF1, TIMING_ESP_DIFF2;
+	int TIMING_ESP_DIFF2_A, TIMING_ESP_DIFF2_B, TIMING_ESP_DIFF2_C, TIMING_ESP_DIFF2_D, TIMING_ESP_DIFF2_E;
+	int TIMING_ESP_PREC1, TIMING_ESP_PREC2, TIMING_ESP_PRECW, TIMING_WSUM_GETSHIFT, TIMING_DIFF2_GETSHIFT, TIMING_WSUM_SCALE, TIMING_WSUM_LOCALSUMS;
 	int TIMING_ESP_WEIGHT1, TIMING_ESP_WEIGHT2, TIMING_WEIGHT_EXP, TIMING_WEIGHT_SORT, TIMING_ESP_WSUM;
+	int TIMING_EXTRA1, TIMING_EXTRA2, TIMING_EXTRA3;
 #endif
 
 public:
@@ -476,13 +478,13 @@ public:
 	/* Calculates the sum of all individual power spectra and the average of all images for initial sigma_noise estimation
 	 * The rank is passed so that if one splits the data into random halves one can know which random half to treat
 	 */
-	void calculateSumOfPowerSpectraAndAverageImage(MultidimArray<double> &Mavg, bool myverb = true);
+	void calculateSumOfPowerSpectraAndAverageImage(MultidimArray<DOUBLE> &Mavg, bool myverb = true);
 
 	/** Use the sum of the individual power spectra to calculate their average and set this in sigma2_noise
 	 * Also subtract the power spectrum of the average images,
 	 * and if (do_average_unaligned) then also set Mavg to all Iref
 	 */
-	void setSigmaNoiseEstimatesAndSetAverageImage(MultidimArray<double> &Mavg);
+	void setSigmaNoiseEstimatesAndSetAverageImage(MultidimArray<DOUBLE> &Mavg);
 
 	/* Perform an initial low-pass filtering of the references
 	 * Note that because of the MAP estimation, this is not necessary inside the refinement
@@ -510,11 +512,20 @@ public:
 	/* Check whether everything fits into memory, possibly adjust nr_pool and setup thread task managers */
 	void expectationSetupCheckMemory(bool myverb = true);
 
+	/* For on-the-fly shifts, precalculates AB-matrices */
+	void precalculateABMatrices();
+
 	/* Perform the expectation integration over all k, phi and series elements for a number (some) of pooled particles
 	 * The number of pooled particles is determined by max_nr_pool and some memory checks in expectationSetup()
 	 */
 	void expectationSomeParticles(long int my_first_particle, long int my_last_particle);
 
+	/* Perform expectation step for some particles using threads */
+	void doThreadExpectationSomeParticles(int thread_id);
+
+	/* Perform the expectation integration over all k, phi and series elements for a given particle */
+	void expectationOneParticle(long int my_ori_particle, int thread_id);
+
 	/* Maximization step
 	 * Updates the current model: reconstructs and updates all other model parameter
 	 */
@@ -546,45 +557,103 @@ public:
 	 */
 	void updateImageSizeAndResolutionPointers();
 
-	/* Calculates the PDF of the in-plane translation
-	 * assuming a 2D (or 3D) Gaussian centered at (0,0) and with stddev of mymodel.sigma2_offset
-	 */
-	double calculatePdfOffset(Matrix1D<double> offset, Matrix1D<double> prior);
-
 	/* From the vectors of Fourier transforms of the images, calculate running averages over the movie frames
 	 */
-	void calculateRunningAveragesOfMovieFrames();
+	void calculateRunningAveragesOfMovieFrames(long int my_ori_particle,
+		std::vector<MultidimArray<Complex > > &exp_Fimgs,
+		std::vector<MultidimArray<DOUBLE> > &exp_power_imgs,
+		std::vector<DOUBLE> &exp_highres_Xi2_imgs);
 
 	/* Read image and its metadata from disc (threaded over all pooled particles)
 	 */
-	void doThreadGetFourierTransformsAndCtfs(int thread_id);
+	void getFourierTransformsAndCtfs(long int my_ori_particle, int metadata_offset,
+			std::vector<MultidimArray<Complex > > &exp_Fimgs,
+			std::vector<MultidimArray<Complex > > &exp_Fimgs_nomask,
+			std::vector<MultidimArray<DOUBLE> > &exp_Fctfs,
+			std::vector<Matrix1D<DOUBLE> > &exp_old_offset,
+			std::vector<Matrix1D<DOUBLE> > &exp_prior,
+			std::vector<MultidimArray<DOUBLE> > &exp_power_imgs,
+			std::vector<DOUBLE> &exp_highres_Xi2_imgs,
+			std::vector<int> &exp_pointer_dir_nonzeroprior,
+			std::vector<int> &exp_pointer_psi_nonzeroprior,
+			std::vector<DOUBLE> &exp_directions_prior,
+			std::vector<DOUBLE> &exp_psi_prior);
 
 	/* Store all shifted FourierTransforms in a vector
 	 * also store precalculated 2D matrices with 1/sigma2_noise
 	 */
-	void doThreadPrecalculateShiftedImagesCtfsAndInvSigma2s(int thread_id);
+	void precalculateShiftedImagesCtfsAndInvSigma2s(bool do_also_unmasked, long int my_ori_particle,
+			int exp_current_image_size, int exp_current_oversampling,
+			int exp_itrans_min, int exp_itrans_max,
+			std::vector<MultidimArray<Complex > > &exp_Fimgs,
+			std::vector<MultidimArray<Complex > > &exp_Fimgs_nomask,
+			std::vector<MultidimArray<DOUBLE> > &exp_Fctfs,
+			std::vector<MultidimArray<Complex > > &exp_local_Fimgs_shifted,
+			std::vector<MultidimArray<Complex > > &exp_local_Fimgs_shifted_nomask,
+			std::vector<MultidimArray<DOUBLE> > &exp_local_Fctfs,
+			std::vector<DOUBLE> &exp_local_sqrtXi2,
+			std::vector<MultidimArray<DOUBLE> > &exp_local_Minvsigma2s);
 
 	// Given exp_Mcoarse_significant, check for iorient whether any of the particles has any significant (coarsely sampled) translation
-	bool isSignificantAnyParticleAnyTranslation(long int iorient);
-
-	// Threaded core of getAllSquaredDifferences, where loops over all orientations are parallelised using threads
-	void doThreadGetSquaredDifferencesAllOrientations(int thread_id);
+	bool isSignificantAnyParticleAnyTranslation(long int iorient,
+			int exp_itrans_min, int exp_itrans_max, MultidimArray<bool> &exp_Mcoarse_significant);
 
 	// Get squared differences for all iclass, idir, ipsi and itrans...
-	void getAllSquaredDifferences();
-
-	// Threaded core of convertAllSquaredDifferencesToWeights, where loops over all orientations are parallelised using threads
-	void doThreadConvertSquaredDifferencesToWeightsAllOrientations(int thread_id);
+	void getAllSquaredDifferences(long int my_ori_particle, int exp_current_image_size,
+			int exp_ipass, int exp_current_oversampling, int metadata_offset,
+			int exp_idir_min, int exp_idir_max, int exp_ipsi_min, int exp_ipsi_max,
+			int exp_itrans_min, int exp_itrans_max, int my_iclass_min, int my_iclass_max,
+			std::vector<DOUBLE> &exp_min_diff2,
+			std::vector<DOUBLE> &exp_highres_Xi2_imgs,
+			std::vector<MultidimArray<Complex > > &exp_Fimgs,
+			std::vector<MultidimArray<DOUBLE> > &exp_Fctfs,
+			MultidimArray<DOUBLE> &exp_Mweight,
+			MultidimArray<bool> &exp_Mcoarse_significant,
+			std::vector<int> &exp_pointer_dir_nonzeroprior, std::vector<int> &exp_pointer_psi_nonzeroprior,
+			std::vector<DOUBLE> &exp_directions_prior, std::vector<DOUBLE> &exp_psi_prior,
+			std::vector<MultidimArray<Complex > > &exp_local_Fimgs_shifted,
+			std::vector<MultidimArray<DOUBLE> > &exp_local_Minvsigma2s,
+			std::vector<MultidimArray<DOUBLE> > &exp_local_Fctfs,
+			std::vector<DOUBLE> &exp_local_sqrtXi2);
 
 	// Convert all squared difference terms to weights.
 	// Also calculates exp_sum_weight and, for adaptive approach, also exp_significant_weight
-	void convertAllSquaredDifferencesToWeights();
-
-	// Threaded core of storeWeightedSums, where loops over all orientations are parallelised using threads
-	void doThreadStoreWeightedSumsAllOrientations(int thread_id);
+	void convertAllSquaredDifferencesToWeights(long int my_ori_particle, int exp_ipass,
+			int exp_current_oversampling, int metadata_offset,
+			int exp_idir_min, int exp_idir_max, int exp_ipsi_min, int exp_ipsi_max,
+			int exp_itrans_min, int exp_itrans_max, int my_iclass_min, int my_iclass_max,
+			MultidimArray<DOUBLE> &exp_Mweight, MultidimArray<bool> &exp_Mcoarse_significant,
+			std::vector<DOUBLE> &exp_significant_weight, std::vector<DOUBLE> &exp_sum_weight,
+			std::vector<Matrix1D<DOUBLE> > &exp_old_offset, std::vector<Matrix1D<DOUBLE> > &exp_prior,
+			std::vector<DOUBLE> &exp_min_diff2,
+			std::vector<int> &exp_pointer_dir_nonzeroprior, std::vector<int> &exp_pointer_psi_nonzeroprior,
+			std::vector<DOUBLE> &exp_directions_prior, std::vector<DOUBLE> &exp_psi_prior);
 
 	// Store all relevant weighted sums, also return optimal hidden variables, max_weight and dLL
-	void storeWeightedSums();
+	void storeWeightedSums(long int my_ori_particle, int exp_current_image_size,
+			int exp_current_oversampling, int metadata_offset,
+			int exp_idir_min, int exp_idir_max, int exp_ipsi_min, int exp_ipsi_max,
+			int exp_itrans_min, int exp_itrans_max, int my_iclass_min, int my_iclass_max,
+			std::vector<DOUBLE> &exp_min_diff2,
+			std::vector<DOUBLE> &exp_highres_Xi2_imgs,
+			std::vector<MultidimArray<Complex > > &exp_Fimgs,
+			std::vector<MultidimArray<Complex > > &exp_Fimgs_nomask,
+			std::vector<MultidimArray<DOUBLE> > &exp_Fctfs,
+			std::vector<MultidimArray<DOUBLE> > &exp_power_imgs,
+			std::vector<Matrix1D<DOUBLE> > &exp_old_offset,
+			std::vector<Matrix1D<DOUBLE> > &exp_prior,
+			MultidimArray<DOUBLE> &exp_Mweight,
+			MultidimArray<bool> &exp_Mcoarse_significant,
+			std::vector<DOUBLE> &exp_significant_weight,
+			std::vector<DOUBLE> &exp_sum_weight,
+			std::vector<DOUBLE> &exp_max_weight,
+			std::vector<int> &exp_pointer_dir_nonzeroprior, std::vector<int> &exp_pointer_psi_nonzeroprior,
+			std::vector<DOUBLE> &exp_directions_prior, std::vector<DOUBLE> &exp_psi_prior,
+			std::vector<MultidimArray<Complex > > &exp_local_Fimgs_shifted,
+			std::vector<MultidimArray<Complex > > &exp_local_Fimgs_shifted_nomask,
+			std::vector<MultidimArray<DOUBLE> > &exp_local_Minvsigma2s,
+			std::vector<MultidimArray<DOUBLE> > &exp_local_Fctfs,
+			std::vector<DOUBLE> &exp_local_sqrtXi2);
 
 	/** Monitor the changes in the optimal translations, orientations and class assignments for some particles */
 	void monitorHiddenVariableChanges(long int my_first_ori_particle, long int my_last_ori_particle);
@@ -613,19 +682,7 @@ public:
 
 };
 
-// Global call to threaded core of doThreadGetFourierTransformsAndCtfs
-void globalThreadGetFourierTransformsAndCtfs(ThreadArgument &thArg);
-
-// Global call to threaded core of doPrecalculateShiftedImagesCtfsAndInvSigma2s
-void globalThreadPrecalculateShiftedImagesCtfsAndInvSigma2s(ThreadArgument &thArg);
-
-// Global call to threaded core of getAllSquaredDifferences, where loops over all orientations are parallelised using threads
-void globalThreadGetSquaredDifferencesAllOrientations(ThreadArgument &thArg);
-
-// Global call to threaded core of convertAllSquaredDifferencesToWeights, where loops over all orientations are parallelised using threads
-void globalThreadConvertSquaredDifferencesToWeightsAllOrientations(ThreadArgument &thArg);
-
-// Global call to threaded core of convertAllSquaredDifferencesToWeights, where loops over all orientations are parallelised using threads
-void globalThreadConvertSquaredDifferencesToWeightsAllOrientations(ThreadArgument &thArg);
+// Global call to threaded core of doThreadExpectationSomeParticles
+void globalThreadExpectationSomeParticles(ThreadArgument &thArg);
 
 #endif /* MAXLIK_H_ */
diff --git a/src/ml_optimiser_mpi.cpp b/src/ml_optimiser_mpi.cpp
index 04a35cc..080e905 100644
--- a/src/ml_optimiser_mpi.cpp
+++ b/src/ml_optimiser_mpi.cpp
@@ -72,7 +72,7 @@ void MlOptimiserMpi::initialise()
 	{
 		// Read in sigma_noise spetrum from file DEVELOPMENTAL!!! FOR DEBUGGING ONLY....
 		MetaDataTable MDsigma;
-		double val;
+		DOUBLE val;
 		int idx;
 		MDsigma.read(fn_sigma);
 		FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDsigma)
@@ -93,7 +93,7 @@ void MlOptimiserMpi::initialise()
 	}
 	else if (do_calculate_initial_sigma_noise || do_average_unaligned)
 	{
-		MultidimArray<double> Mavg;
+		MultidimArray<DOUBLE> Mavg;
 		// Calculate initial sigma noise model from power_class spectra of the individual images
 		// This is done in parallel
 		//std::cout << " Hello world1! I am node " << node->rank << " out of " << node->size <<" and my hostname= "<< getenv("HOSTNAME")<< std::endl;
@@ -105,7 +105,7 @@ void MlOptimiserMpi::initialise()
 		//std::cout << " Hello world3! I am node " << node->rank << " out of " << node->size <<" and my hostname= "<< getenv("HOSTNAME")<< std::endl;
 	}
 
-    MlOptimiser::initialLowPassFilterReferences();
+   MlOptimiser::initialLowPassFilterReferences();
 
 	// Initialise the data_versus_prior ratio to get the initial current_size right
 	if (iter == 0)
@@ -239,7 +239,7 @@ void MlOptimiserMpi::initialiseWorkLoad()
 #endif
 }
 
-void MlOptimiserMpi::calculateSumOfPowerSpectraAndAverageImage(MultidimArray<double> &Mavg)
+void MlOptimiserMpi::calculateSumOfPowerSpectraAndAverageImage(MultidimArray<DOUBLE> &Mavg)
 {
 
 	// First calculate the sum of all individual power spectra on each subset
@@ -249,9 +249,9 @@ void MlOptimiserMpi::calculateSumOfPowerSpectraAndAverageImage(MultidimArray<dou
 	// When splitting the data into two random halves, perform two passes: one for each subset
 	int nr_subsets = (do_split_random_halves) ? 2 : 1;
 
-	MultidimArray<double> Msum, MsumI;
+	MultidimArray<DOUBLE> Msum, MsumI;
 	MultidimArray<int> Mnr, Msumnr;
-	double dsum;
+	DOUBLE dsum;
 	int isum;
 	MPI_Status status;
 
@@ -280,24 +280,24 @@ void MlOptimiserMpi::calculateSumOfPowerSpectraAndAverageImage(MultidimArray<dou
 			if (node->rank == other_slave)
 			{
 				//std::cerr << "Sending from "<<other_slave<< " to "<<my_first_slave << std::endl;
-				node->relion_MPI_Send(MULTIDIM_ARRAY(wsum_model.sigma2_noise[0]), MULTIDIM_SIZE(wsum_model.sigma2_noise[0]), MPI_DOUBLE, my_first_slave, MPITAG_PACK, MPI_COMM_WORLD);
-				node->relion_MPI_Send(MULTIDIM_ARRAY(Mavg), MULTIDIM_SIZE(Mavg), MPI_DOUBLE, my_first_slave, MPITAG_IMAGE, MPI_COMM_WORLD);
+				node->relion_MPI_Send(MULTIDIM_ARRAY(wsum_model.sigma2_noise[0]), MULTIDIM_SIZE(wsum_model.sigma2_noise[0]), MY_MPI_DOUBLE, my_first_slave, MPITAG_PACK, MPI_COMM_WORLD);
+				node->relion_MPI_Send(MULTIDIM_ARRAY(Mavg), MULTIDIM_SIZE(Mavg), MY_MPI_DOUBLE, my_first_slave, MPITAG_IMAGE, MPI_COMM_WORLD);
 				Mnr.resize(mymodel.nr_groups);
 				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY1D(Mnr)
 				{
 					DIRECT_A1D_ELEM(Mnr, i) = mymodel.nr_particles_group[i];
         		}
 				node->relion_MPI_Send(MULTIDIM_ARRAY(Mnr), MULTIDIM_SIZE(Mnr), MPI_INT, my_first_slave, MPITAG_METADATA, MPI_COMM_WORLD);
-				node->relion_MPI_Send(&wsum_model.sumw_group[0], 1, MPI_DOUBLE, my_first_slave, MPITAG_DOUBLE, MPI_COMM_WORLD);
+				node->relion_MPI_Send(&wsum_model.sumw_group[0], 1, MY_MPI_DOUBLE, my_first_slave, MPITAG_DOUBLE, MPI_COMM_WORLD);
 
     		}
 			else if (node->rank == my_first_slave)
 			{
 				//std::cerr << "Receiving at "<<my_first_slave<< " from "<<other_slave<<std::endl;
-				node->relion_MPI_Recv(MULTIDIM_ARRAY(wsum_model.sigma2_noise[0]), MULTIDIM_SIZE(wsum_model.sigma2_noise[0]), MPI_DOUBLE, other_slave, MPITAG_PACK, MPI_COMM_WORLD, status);
-				node->relion_MPI_Recv(MULTIDIM_ARRAY(Mavg), MULTIDIM_SIZE(Mavg), MPI_DOUBLE, other_slave, MPITAG_IMAGE, MPI_COMM_WORLD, status);
+				node->relion_MPI_Recv(MULTIDIM_ARRAY(wsum_model.sigma2_noise[0]), MULTIDIM_SIZE(wsum_model.sigma2_noise[0]), MY_MPI_DOUBLE, other_slave, MPITAG_PACK, MPI_COMM_WORLD, status);
+				node->relion_MPI_Recv(MULTIDIM_ARRAY(Mavg), MULTIDIM_SIZE(Mavg), MY_MPI_DOUBLE, other_slave, MPITAG_IMAGE, MPI_COMM_WORLD, status);
 				node->relion_MPI_Recv(MULTIDIM_ARRAY(Mnr), MULTIDIM_SIZE(Mnr), MPI_INT, other_slave, MPITAG_METADATA, MPI_COMM_WORLD, status);
-				node->relion_MPI_Recv(&wsum_model.sumw_group[0], 1, MPI_DOUBLE, other_slave, MPITAG_DOUBLE, MPI_COMM_WORLD, status);
+				node->relion_MPI_Recv(&wsum_model.sumw_group[0], 1, MY_MPI_DOUBLE, other_slave, MPITAG_DOUBLE, MPI_COMM_WORLD, status);
 				// Add the contribution of this other slave
 				Msum  += wsum_model.sigma2_noise[0];
 				MsumI += Mavg;
@@ -313,18 +313,18 @@ void MlOptimiserMpi::calculateSumOfPowerSpectraAndAverageImage(MultidimArray<dou
 			if (node->rank == my_first_slave)
 			{
 				//std::cerr << "Sending from my_first_slave"<<my_first_slave<< " to other_slave "<<other_slave<< std::endl;
-				node->relion_MPI_Send(MULTIDIM_ARRAY(Msum), MULTIDIM_SIZE(Msum), MPI_DOUBLE, other_slave, MPITAG_PACK, MPI_COMM_WORLD);
+				node->relion_MPI_Send(MULTIDIM_ARRAY(Msum), MULTIDIM_SIZE(Msum), MY_MPI_DOUBLE, other_slave, MPITAG_PACK, MPI_COMM_WORLD);
 				node->relion_MPI_Send(MULTIDIM_ARRAY(Msumnr), MULTIDIM_SIZE(Msumnr), MPI_INT, other_slave, MPITAG_METADATA, MPI_COMM_WORLD);
-				node->relion_MPI_Send(MULTIDIM_ARRAY(MsumI), MULTIDIM_SIZE(MsumI), MPI_DOUBLE, other_slave, MPITAG_IMAGE, MPI_COMM_WORLD);
-				node->relion_MPI_Send(&dsum, 1, MPI_DOUBLE, other_slave, MPITAG_DOUBLE, MPI_COMM_WORLD);
+				node->relion_MPI_Send(MULTIDIM_ARRAY(MsumI), MULTIDIM_SIZE(MsumI), MY_MPI_DOUBLE, other_slave, MPITAG_IMAGE, MPI_COMM_WORLD);
+				node->relion_MPI_Send(&dsum, 1, MY_MPI_DOUBLE, other_slave, MPITAG_DOUBLE, MPI_COMM_WORLD);
 			}
 			else if (node->rank == other_slave)
 			{
 				//std::cerr << "Receiving at other_slave "<<other_slave<<" from my_first_slave "<<my_first_slave<< std::endl;
-				node->relion_MPI_Recv(MULTIDIM_ARRAY(wsum_model.sigma2_noise[0]), MULTIDIM_SIZE(wsum_model.sigma2_noise[0]), MPI_DOUBLE, my_first_slave, MPITAG_PACK, MPI_COMM_WORLD, status);
+				node->relion_MPI_Recv(MULTIDIM_ARRAY(wsum_model.sigma2_noise[0]), MULTIDIM_SIZE(wsum_model.sigma2_noise[0]), MY_MPI_DOUBLE, my_first_slave, MPITAG_PACK, MPI_COMM_WORLD, status);
 				node->relion_MPI_Recv(MULTIDIM_ARRAY(Mnr), MULTIDIM_SIZE(Mnr), MPI_INT, my_first_slave, MPITAG_METADATA, MPI_COMM_WORLD, status);
-				node->relion_MPI_Recv(MULTIDIM_ARRAY(Mavg), MULTIDIM_SIZE(Mavg), MPI_DOUBLE, my_first_slave, MPITAG_IMAGE, MPI_COMM_WORLD, status);
-				node->relion_MPI_Recv(&wsum_model.sumw_group[0], 1, MPI_DOUBLE, my_first_slave, MPITAG_DOUBLE, MPI_COMM_WORLD, status);
+				node->relion_MPI_Recv(MULTIDIM_ARRAY(Mavg), MULTIDIM_SIZE(Mavg), MY_MPI_DOUBLE, my_first_slave, MPITAG_IMAGE, MPI_COMM_WORLD, status);
+				node->relion_MPI_Recv(&wsum_model.sumw_group[0], 1, MY_MPI_DOUBLE, my_first_slave, MPITAG_DOUBLE, MPI_COMM_WORLD, status);
 				// Unpack Mnr on the other slave
 				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY1D(Mnr)
 				{
@@ -357,11 +357,11 @@ void MlOptimiserMpi::expectation()
 	std::cerr << "MlOptimiserMpi::expectation: Entering " << std::endl;
 #endif
 
-	MultidimArray<long int> first_last_nr_images(3);
-	MultidimArray<double> metadata;
+	MultidimArray<long int> first_last_nr_images(6);
+	MultidimArray<DOUBLE> metadata;
 	int first_slave = 1;
 	// Use maximum of 100 particles for 3D and 10 particles for 2D estimations
-	int n_trials_acc = (mymodel.ref_dim==3) ? 100 : 10;
+	int n_trials_acc = (mymodel.ref_dim==3 && mymodel.data_dim != 3) ? 100 : 10;
 	n_trials_acc = XMIPP_MIN(n_trials_acc, mydata.numberOfOriginalParticles());
 	MPI_Status status;
 
@@ -382,7 +382,7 @@ void MlOptimiserMpi::expectation()
 		// Many small new's are not returned to the OS upon free-ing them. To force this, use the following call
 		// from http://stackoverflow.com/questions/10943907/linux-allocator-does-not-release-small-chunks-of-memory
 #if !defined(__APPLE__)
-                malloc_trim(0);
+		malloc_trim(0);
 #endif
 
 	}
@@ -390,29 +390,44 @@ void MlOptimiserMpi::expectation()
 	// C. Calculate expected angular errors
 	// Do not do this for maxCC
 	// Only the first (reconstructing) slave (i.e. from half1) calculates expected angular errors
-	if (!(iter==1 && do_firstiter_cc) &&  !(do_skip_align || do_skip_rotate) )
+	////TMP if (!(iter==1 && do_firstiter_cc) && (mymodel.data_dim == 2) && !do_skip_align )
+	if (!(iter==1 && do_firstiter_cc) && !(do_skip_align || do_skip_rotate) )
 	{
-		int my_nr_images;
+		int my_nr_images, length_fn_ctf;
 		if (node->isMaster())
 		{
 			// Master sends metadata (but not imagedata) for first 100 particles to first_slave (for calculateExpectedAngularErrors)
 			MlOptimiser::getMetaAndImageDataSubset(0, n_trials_acc-1, false);
 			my_nr_images = YSIZE(exp_metadata);
 			node->relion_MPI_Send(&my_nr_images, 1, MPI_INT, first_slave, MPITAG_JOB_REQUEST, MPI_COMM_WORLD);
-			node->relion_MPI_Send(MULTIDIM_ARRAY(exp_metadata), MULTIDIM_SIZE(exp_metadata), MPI_DOUBLE, first_slave, MPITAG_METADATA, MPI_COMM_WORLD);
+			node->relion_MPI_Send(MULTIDIM_ARRAY(exp_metadata), MULTIDIM_SIZE(exp_metadata), MY_MPI_DOUBLE, first_slave, MPITAG_METADATA, MPI_COMM_WORLD);
+			// Also send exp_fn_ctfs if necessary
+			length_fn_ctf = exp_fn_img.length() + 1; // +1 to include \0 at the end of the string
+			node->relion_MPI_Send(&length_fn_ctf, 1, MPI_INT, first_slave, MPITAG_JOB_REQUEST, MPI_COMM_WORLD);
+			if (length_fn_ctf > 1)
+				node->relion_MPI_Send((void*)exp_fn_ctf.c_str(), length_fn_ctf, MPI_CHAR, first_slave, MPITAG_METADATA, MPI_COMM_WORLD);
 		}
 		else if (node->rank == first_slave)
 		{
 			// Slave has to receive all metadata from the master!
 			node->relion_MPI_Recv(&my_nr_images, 1, MPI_INT, 0, MPITAG_JOB_REQUEST, MPI_COMM_WORLD, status);
 			exp_metadata.resize(my_nr_images, METADATA_LINE_LENGTH);
-			node->relion_MPI_Recv(MULTIDIM_ARRAY(exp_metadata), MULTIDIM_SIZE(exp_metadata), MPI_DOUBLE, 0, MPITAG_METADATA, MPI_COMM_WORLD, status);
+			node->relion_MPI_Recv(MULTIDIM_ARRAY(exp_metadata), MULTIDIM_SIZE(exp_metadata), MY_MPI_DOUBLE, 0, MPITAG_METADATA, MPI_COMM_WORLD, status);
+			node->relion_MPI_Recv(&length_fn_ctf, 1, MPI_INT, 0, MPITAG_JOB_REQUEST, MPI_COMM_WORLD, status);
+			if (length_fn_ctf > 1)
+			{
+				char* rec_buf2;
+				rec_buf2 = (char *) malloc(length_fn_ctf);
+				node->relion_MPI_Recv(rec_buf2, length_fn_ctf, MPI_CHAR, 0, MPITAG_METADATA, MPI_COMM_WORLD, status);
+				exp_fn_ctf = rec_buf2;
+				free(rec_buf2);
+			}
 			calculateExpectedAngularErrors(0, n_trials_acc-1);
 		}
 
 		// The reconstructing slave Bcast acc_rottilt, acc_psi, acc_trans to all other nodes!
-		node->relion_MPI_Bcast(&acc_rot, 1, MPI_DOUBLE, first_slave, MPI_COMM_WORLD);
-		node->relion_MPI_Bcast(&acc_trans, 1, MPI_DOUBLE, first_slave, MPI_COMM_WORLD);
+		node->relion_MPI_Bcast(&acc_rot, 1, MY_MPI_DOUBLE, first_slave, MPI_COMM_WORLD);
+		node->relion_MPI_Bcast(&acc_trans, 1, MY_MPI_DOUBLE, first_slave, MPI_COMM_WORLD);
 	}
 
 	// D. Update the angular sampling (all nodes except master)
@@ -422,18 +437,21 @@ void MlOptimiserMpi::expectation()
 	}
 	node->relion_MPI_Bcast(&has_fine_enough_angular_sampling, 1, MPI_INT, first_slave, MPI_COMM_WORLD);
 
-	// E. All nodes, except the master, check memory
+	// E. All nodes, except the master, check memory and precalculate AB-matrices for on-the-fly shifts
 	if (!node->isMaster())
 	{
-		// Check whether everything fits into memory, possibly adjust nr_pool and setup thread task managers
+		// Check whether everything fits into memory
 		MlOptimiser::expectationSetupCheckMemory(node->rank == first_slave);
+
+		// F. Precalculate AB-matrices for on-the-fly shifts
+		if (do_shifts_onthefly)
+			precalculateABMatrices();
 	}
-	// Slave 1 sends nr_pool and has_converged to everyone else (in particular the master needs it!)
-	// nr_pool was set by all slaves, but not the master, in MlOptimiser::expectationSetupCheckMemory
-	node->relion_MPI_Bcast(&nr_pool, 1, MPI_INT, first_slave, MPI_COMM_WORLD);
+	// Slave 1 sends has_converged to everyone else (in particular the master needs it!)
 	node->relion_MPI_Bcast(&has_converged, 1, MPI_INT, first_slave, MPI_COMM_WORLD);
 	node->relion_MPI_Bcast(&do_join_random_halves, 1, MPI_INT, first_slave, MPI_COMM_WORLD);
 
+
 	// Wait until expected angular errors have been calculated
 	MPI_Barrier(MPI_COMM_WORLD);
 	sleep(1);
@@ -442,6 +460,9 @@ void MlOptimiserMpi::expectation()
 #define JOB_FIRST (first_last_nr_images(0))
 #define JOB_LAST  (first_last_nr_images(1))
 #define JOB_NIMG  (first_last_nr_images(2))
+#define JOB_LEN_FN_IMG  (first_last_nr_images(3))
+#define JOB_LEN_FN_CTF  (first_last_nr_images(4))
+#define JOB_LEN_FN_RECIMG  (first_last_nr_images(5))
 #define JOB_NPAR  (JOB_LAST - JOB_FIRST + 1)
 
     if (node->isMaster())
@@ -481,7 +502,7 @@ void MlOptimiserMpi::expectation()
 				if (JOB_NIMG > 0)
 				{
 					exp_metadata.resize(JOB_NIMG, METADATA_LINE_LENGTH);
-					node->relion_MPI_Recv(MULTIDIM_ARRAY(exp_metadata), MULTIDIM_SIZE(exp_metadata), MPI_DOUBLE, this_slave, MPITAG_METADATA, MPI_COMM_WORLD, status);
+					node->relion_MPI_Recv(MULTIDIM_ARRAY(exp_metadata), MULTIDIM_SIZE(exp_metadata), MY_MPI_DOUBLE, this_slave, MPITAG_METADATA, MPI_COMM_WORLD, status);
 
 					// The master monitors the changes in the optimal orientations and classes
 					monitorHiddenVariableChanges(JOB_FIRST, JOB_LAST);
@@ -526,8 +547,11 @@ void MlOptimiserMpi::expectation()
 				if (my_nr_ori_particles_done < mydata.numberOfOriginalParticles(random_subset))
 				{
 
-					MlOptimiser::getMetaAndImageDataSubset(JOB_FIRST, JOB_LAST);
+					MlOptimiser::getMetaAndImageDataSubset(JOB_FIRST, JOB_LAST, !do_parallel_disc_io);
 					JOB_NIMG = YSIZE(exp_metadata);
+					JOB_LEN_FN_IMG = exp_fn_img.length() + 1; // +1 to include \0 at the end of the string
+					JOB_LEN_FN_CTF = exp_fn_ctf.length() + 1;
+					JOB_LEN_FN_RECIMG = exp_fn_recimg.length() + 1;
 				}
 				else
 				{
@@ -535,6 +559,9 @@ void MlOptimiserMpi::expectation()
 					JOB_FIRST = -1;
 					JOB_LAST = -1;
 					JOB_NIMG = 0;
+					JOB_LEN_FN_IMG = 0;
+					JOB_LEN_FN_CTF = 0;
+					JOB_LEN_FN_RECIMG = 0;
 					exp_metadata.clear();
 					exp_imagedata.clear();
 
@@ -550,8 +577,21 @@ void MlOptimiserMpi::expectation()
 				// Master also sends the required metadata and imagedata for this job
 				if (JOB_NIMG > 0)
 				{
-					node->relion_MPI_Send(MULTIDIM_ARRAY(exp_metadata), MULTIDIM_SIZE(exp_metadata), MPI_DOUBLE, this_slave, MPITAG_METADATA, MPI_COMM_WORLD);
-					node->relion_MPI_Send(MULTIDIM_ARRAY(exp_imagedata), MULTIDIM_SIZE(exp_imagedata), MPI_DOUBLE, this_slave, MPITAG_IMAGE, MPI_COMM_WORLD);
+					node->relion_MPI_Send(MULTIDIM_ARRAY(exp_metadata), MULTIDIM_SIZE(exp_metadata), MY_MPI_DOUBLE, this_slave, MPITAG_METADATA, MPI_COMM_WORLD);
+					if (do_parallel_disc_io)
+					{
+						node->relion_MPI_Send((void*)exp_fn_img.c_str(), JOB_LEN_FN_IMG, MPI_CHAR, this_slave, MPITAG_METADATA, MPI_COMM_WORLD);
+						// Send filenames of images to the slaves
+						if (JOB_LEN_FN_CTF > 1)
+							node->relion_MPI_Send((void*)exp_fn_ctf.c_str(), JOB_LEN_FN_CTF, MPI_CHAR, this_slave, MPITAG_METADATA, MPI_COMM_WORLD);
+						if (JOB_LEN_FN_RECIMG > 1)
+							node->relion_MPI_Send((void*)exp_fn_recimg.c_str(), JOB_LEN_FN_RECIMG, MPI_CHAR, this_slave, MPITAG_METADATA, MPI_COMM_WORLD);
+					}
+					else
+					{
+						// Send imagedata to the slaves
+						node->relion_MPI_Send(MULTIDIM_ARRAY(exp_imagedata), MULTIDIM_SIZE(exp_imagedata), MY_MPI_DOUBLE, this_slave, MPITAG_IMAGE, MPI_COMM_WORLD);
+					}
 				}
 
 				// Update the total number of particles that has been done already
@@ -585,12 +625,22 @@ void MlOptimiserMpi::expectation()
 			JOB_FIRST = 0;
 			JOB_LAST = -1; // So that initial nr_particles (=JOB_LAST-JOB_FIRST+1) is zero!
 			JOB_NIMG = 0;
+			JOB_LEN_FN_IMG = 0;
+			JOB_LEN_FN_CTF = 0;
+			JOB_LEN_FN_RECIMG = 0;
 			node->relion_MPI_Send(MULTIDIM_ARRAY(first_last_nr_images), MULTIDIM_SIZE(first_last_nr_images), MPI_LONG, 0, MPITAG_JOB_REQUEST, MPI_COMM_WORLD);
 
 			while (true)
 			{
+#ifdef TIMING
+				timer.tic(TIMING_MPISLAVEWAIT1);
+#endif
+
 				//Receive a new bunch of particles
 				node->relion_MPI_Recv(MULTIDIM_ARRAY(first_last_nr_images), MULTIDIM_SIZE(first_last_nr_images), MPI_LONG, 0, MPITAG_JOB_REPLY, MPI_COMM_WORLD, status);
+#ifdef TIMING
+				timer.toc(TIMING_MPISLAVEWAIT1);
+#endif
 
 				//Check whether I am done
 				if (JOB_NIMG <= 0)
@@ -604,26 +654,94 @@ void MlOptimiserMpi::expectation()
 				}
 				else
 				{
+#ifdef TIMING
+					timer.tic(TIMING_MPISLAVEWAIT2);
+#endif
 					// Also receive the imagedata and the metadata for these images from the master
 					exp_metadata.resize(JOB_NIMG, METADATA_LINE_LENGTH);
-					if (has_converged && do_use_reconstruct_images)
-						exp_imagedata.resize(2*JOB_NIMG, mymodel.ori_size, mymodel.ori_size);
+					node->relion_MPI_Recv(MULTIDIM_ARRAY(exp_metadata), MULTIDIM_SIZE(exp_metadata), MY_MPI_DOUBLE, 0, MPITAG_METADATA, MPI_COMM_WORLD, status);
+
+					// Receive the image filenames or the exp_imagedata
+					if (do_parallel_disc_io)
+					{
+						// Resize the exp_fn_img strings
+				        char* rec_buf;
+				        rec_buf = (char *) malloc(JOB_LEN_FN_IMG);
+				        node->relion_MPI_Recv(rec_buf, JOB_LEN_FN_IMG, MPI_CHAR, 0, MPITAG_METADATA, MPI_COMM_WORLD, status);
+				        exp_fn_img = rec_buf;
+				        free(rec_buf);
+						if (JOB_LEN_FN_CTF > 1)
+						{
+					        char* rec_buf2;
+					        rec_buf2 = (char *) malloc(JOB_LEN_FN_CTF);
+					        node->relion_MPI_Recv(rec_buf2, JOB_LEN_FN_CTF, MPI_CHAR, 0, MPITAG_METADATA, MPI_COMM_WORLD, status);
+					        exp_fn_ctf = rec_buf2;
+					        free(rec_buf2);
+
+						}
+						if (JOB_LEN_FN_RECIMG > 1)
+						{
+					        char* rec_buf3;
+					        rec_buf3 = (char *) malloc(JOB_LEN_FN_RECIMG);
+					        node->relion_MPI_Recv(rec_buf3, JOB_LEN_FN_RECIMG, MPI_CHAR, 0, MPITAG_METADATA, MPI_COMM_WORLD, status);
+					        exp_fn_recimg = rec_buf3;
+					        free(rec_buf3);
+						}
+					}
 					else
-						exp_imagedata.resize(JOB_NIMG, mymodel.ori_size, mymodel.ori_size);
+					{
+						// resize the exp_imagedata array
+						if (mymodel.data_dim == 3)
+						{
 
-					node->relion_MPI_Recv(MULTIDIM_ARRAY(exp_metadata), MULTIDIM_SIZE(exp_metadata), MPI_DOUBLE, 0, MPITAG_METADATA, MPI_COMM_WORLD, status);
-					node->relion_MPI_Recv(MULTIDIM_ARRAY(exp_imagedata), MULTIDIM_SIZE(exp_imagedata), MPI_DOUBLE, 0, MPITAG_IMAGE, MPI_COMM_WORLD, status);
+							if (do_ctf_correction)
+							{
+								if (has_converged && do_use_reconstruct_images)
+									exp_imagedata.resize(3*mymodel.ori_size, mymodel.ori_size, mymodel.ori_size);
+								else
+									exp_imagedata.resize(2*mymodel.ori_size, mymodel.ori_size, mymodel.ori_size);
+							}
+							else
+							{
+								if (has_converged && do_use_reconstruct_images)
+									exp_imagedata.resize(2*mymodel.ori_size, mymodel.ori_size, mymodel.ori_size);
+								else
+									exp_imagedata.resize(mymodel.ori_size, mymodel.ori_size, mymodel.ori_size);
+							}
+						}
+						else
+						{
+							if (has_converged && do_use_reconstruct_images)
+								exp_imagedata.resize(2*JOB_NIMG, mymodel.ori_size, mymodel.ori_size);
+							else
+								exp_imagedata.resize(JOB_NIMG, mymodel.ori_size, mymodel.ori_size);
+						}
+						node->relion_MPI_Recv(MULTIDIM_ARRAY(exp_imagedata), MULTIDIM_SIZE(exp_imagedata), MY_MPI_DOUBLE, 0, MPITAG_IMAGE, MPI_COMM_WORLD, status);
+					}
 
 					// Now process these images
 #ifdef DEBUG_MPIEXP
 					std::cerr << " SLAVE EXECUTING node->rank= " << node->rank << " JOB_FIRST= " << JOB_FIRST << " JOB_LAST= " << JOB_LAST << std::endl;
 #endif
+#ifdef TIMING
+					timer.toc(TIMING_MPISLAVEWAIT2);
+					timer.tic(TIMING_MPISLAVEWORK);
+#endif
 					expectationSomeParticles(JOB_FIRST, JOB_LAST);
+#ifdef TIMING
+					timer.toc(TIMING_MPISLAVEWORK);
+					timer.tic(TIMING_MPISLAVEWAIT3);
+#endif
 
 					// Report to the master how many particles I have processed
 					node->relion_MPI_Send(MULTIDIM_ARRAY(first_last_nr_images), MULTIDIM_SIZE(first_last_nr_images), MPI_LONG, 0, MPITAG_JOB_REQUEST, MPI_COMM_WORLD);
 					// Also send the metadata belonging to those
-					node->relion_MPI_Send(MULTIDIM_ARRAY(exp_metadata), MULTIDIM_SIZE(exp_metadata), MPI_DOUBLE, 0, MPITAG_METADATA, MPI_COMM_WORLD);
+					node->relion_MPI_Send(MULTIDIM_ARRAY(exp_metadata), MULTIDIM_SIZE(exp_metadata), MY_MPI_DOUBLE, 0, MPITAG_METADATA, MPI_COMM_WORLD);
+
+#ifdef TIMING
+					timer.toc(TIMING_MPISLAVEWAIT3);
+#endif
+
 				}
 
 			}
@@ -648,7 +766,6 @@ void MlOptimiserMpi::expectation()
     timer.tic(TIMING_MPIWAIT);
     node->barrierWait();
     timer.toc(TIMING_MPIWAIT);
-    timer.tic(TIMING_MPIPACK);
 #endif
 
 	// Wait until expected angular errors have been calculated
@@ -672,7 +789,10 @@ void MlOptimiserMpi::expectation()
 void MlOptimiserMpi::combineAllWeightedSumsViaFile()
 {
 
-	MultidimArray<double> Mpack;
+#ifdef TIMING
+    timer.tic(TIMING_MPICOMBINEDISC);
+#endif
+	MultidimArray<DOUBLE> Mpack;
 	FileName fn_pack;
 
 	int nr_subsets = (do_split_random_halves) ? 2 : 1;
@@ -694,11 +814,14 @@ void MlOptimiserMpi::combineAllWeightedSumsViaFile()
 				fn_pack.compose(fn_out+"_rank", node->rank, "tmp");
 				if (fn_scratch != "")
 					fn_pack = fn_scratch + "/" + fn_pack;
+
 				Mpack.writeBinary(fn_pack);
 				//std::cerr << "Rank "<< node->rank <<" has written: "<<fn_pack << " sum= "<<Mpack.sum()<< std::endl;
 			}
-			MPI_Barrier(MPI_COMM_WORLD);
+			if (!do_parallel_disc_io)
+				MPI_Barrier(MPI_COMM_WORLD);
 		}
+		MPI_Barrier(MPI_COMM_WORLD);
 
 		// C. First slave of each subset reads all other slaves' Mpack; sum; and write sum to disc
 		// Again, do this SEQUENTIALLY to prevent heavy load on disc I/O
@@ -711,6 +834,7 @@ void MlOptimiserMpi::combineAllWeightedSumsViaFile()
 					fn_pack.compose(fn_out+"_rank", other_slave, "tmp");
 					if (fn_scratch != "")
 						fn_pack = fn_scratch + "/" + fn_pack;
+
 					Mpack.readBinaryAndSum(fn_pack);
 					//std::cerr << "Slave "<<node->rank<<" has read "<<fn_pack<< " sum= "<<Mpack.sum() << std::endl;
 				}
@@ -718,11 +842,14 @@ void MlOptimiserMpi::combineAllWeightedSumsViaFile()
 				fn_pack.compose(fn_out+"_rank", node->rank, "tmp");
 				if (fn_scratch != "")
 					fn_pack = fn_scratch + "/" + fn_pack;
+
 				Mpack.writeBinary(fn_pack);
 				//std::cerr << "Slave "<<node->rank<<" is writing total SUM in "<<fn_pack << std::endl;
 			}
-			MPI_Barrier(MPI_COMM_WORLD);
+			if (!do_parallel_disc_io)
+				MPI_Barrier(MPI_COMM_WORLD);
 		}
+		MPI_Barrier(MPI_COMM_WORLD);
 
 
 		// D. All other slaves read the summed Mpack from their first_slave
@@ -742,11 +869,14 @@ void MlOptimiserMpi::combineAllWeightedSumsViaFile()
 				fn_pack.compose(fn_out+"_rank", first_slave, "tmp");
 				if (fn_scratch != "")
 					fn_pack = fn_scratch + "/" + fn_pack;
+
 				Mpack.readBinary(fn_pack);
 				//std::cerr << "Rank "<< node->rank <<" has read: "<<fn_pack << " sum= "<<Mpack.sum()<< std::endl;
 			}
-			MPI_Barrier(MPI_COMM_WORLD);
+			if (!do_parallel_disc_io)
+				MPI_Barrier(MPI_COMM_WORLD);
 		}
+		MPI_Barrier(MPI_COMM_WORLD);
 
 		// E. All slaves delete their own temporary file
 		// Again, do this SEQUENTIALLY to prevent heavy load on disc I/O
@@ -757,10 +887,12 @@ void MlOptimiserMpi::combineAllWeightedSumsViaFile()
 				fn_pack.compose(fn_out+"_rank", node->rank, "tmp");
 				if (fn_scratch != "")
 					fn_pack = fn_scratch + "/" + fn_pack;
+
 				remove((fn_pack).c_str());
 				//std::cerr << "Rank "<< node->rank <<" has deleted: "<<fn_pack << std::endl;
 			}
-			MPI_Barrier(MPI_COMM_WORLD);
+			if (!do_parallel_disc_io)
+				MPI_Barrier(MPI_COMM_WORLD);
 		}
 
 		// F. Finally all slaves unpack Msum into their wsum_model (do this simultaneously)
@@ -768,14 +900,21 @@ void MlOptimiserMpi::combineAllWeightedSumsViaFile()
 			wsum_model.unpack(Mpack);
 
 	} // end if ((node->size - 1)/nr_subsets > 1)
+#ifdef TIMING
+    timer.toc(TIMING_MPICOMBINEDISC);
+#endif
 
 }
 
 void MlOptimiserMpi::combineAllWeightedSums()
 {
 
-	// Pack all weighted sums in Mpack
-	MultidimArray<double> Mpack, Msum;
+#ifdef TIMING
+    timer.tic(TIMING_MPICOMBINENETW);
+#endif
+
+    // Pack all weighted sums in Mpack
+	MultidimArray<DOUBLE> Mpack, Msum;
 	MPI_Status status;
 
 	// First slave manually sums over all other slaves of it's subset
@@ -830,11 +969,11 @@ void MlOptimiserMpi::combineAllWeightedSums()
 						std::cerr << " AA SEND node->rank= " << node->rank << " MULTIDIM_SIZE(Msum)= "<< MULTIDIM_SIZE(Msum)
 								<< " this_slave= " << this_slave << " other_slave= "<<other_slave << std::endl;
 #endif
-						node->relion_MPI_Send(MULTIDIM_ARRAY(Msum), MULTIDIM_SIZE(Msum), MPI_DOUBLE, other_slave, MPITAG_PACK, MPI_COMM_WORLD);
+						node->relion_MPI_Send(MULTIDIM_ARRAY(Msum), MULTIDIM_SIZE(Msum), MY_MPI_DOUBLE, other_slave, MPITAG_PACK, MPI_COMM_WORLD);
 					}
 					else if (node->rank == other_slave)
 					{
-						MPI_Recv(MULTIDIM_ARRAY(Msum), MULTIDIM_SIZE(Msum), MPI_DOUBLE, this_slave, MPITAG_PACK, MPI_COMM_WORLD, &status);
+						MPI_Recv(MULTIDIM_ARRAY(Msum), MULTIDIM_SIZE(Msum), MY_MPI_DOUBLE, this_slave, MPITAG_PACK, MPI_COMM_WORLD, &status);
 #ifdef DEBUG
 						std::cerr << " AA RECV node->rank= " << node->rank  << " MULTIDIM_SIZE(Msum)= "<< MULTIDIM_SIZE(Msum)
 								<< " this_slave= " << this_slave << " other_slave= "<<other_slave << std::endl;
@@ -852,11 +991,11 @@ void MlOptimiserMpi::combineAllWeightedSums()
 						std::cerr << " BB SEND node->rank= " << node->rank  << " MULTIDIM_SIZE(Msum)= "<< MULTIDIM_SIZE(Msum)
 								<< " this_slave= " << this_slave << " first_slave= "<<first_slave << std::endl;
 #endif
-						node->relion_MPI_Send(MULTIDIM_ARRAY(Msum), MULTIDIM_SIZE(Msum), MPI_DOUBLE, first_slave, MPITAG_PACK, MPI_COMM_WORLD);
+						node->relion_MPI_Send(MULTIDIM_ARRAY(Msum), MULTIDIM_SIZE(Msum), MY_MPI_DOUBLE, first_slave, MPITAG_PACK, MPI_COMM_WORLD);
 					}
 					else if (node->rank == first_slave)
 					{
-						node->relion_MPI_Recv(MULTIDIM_ARRAY(Msum), MULTIDIM_SIZE(Msum), MPI_DOUBLE, this_slave, MPITAG_PACK, MPI_COMM_WORLD, status);
+						node->relion_MPI_Recv(MULTIDIM_ARRAY(Msum), MULTIDIM_SIZE(Msum), MY_MPI_DOUBLE, this_slave, MPITAG_PACK, MPI_COMM_WORLD, status);
 #ifdef DEBUG
 						std::cerr << " BB RECV node->rank= " << node->rank  << " MULTIDIM_SIZE(Msum)= "<< MULTIDIM_SIZE(Msum)
 								<< " this_slave= " << this_slave << " first_slave= "<<first_slave << std::endl;
@@ -880,11 +1019,11 @@ void MlOptimiserMpi::combineAllWeightedSums()
 						std::cerr << " CC SEND node->rank= " << node->rank << " MULTIDIM_SIZE(Msum)= "<< MULTIDIM_SIZE(Msum)
 								<< " this_slave= " << this_slave << " other_slave= "<<other_slave << std::endl;
 #endif
-						node->relion_MPI_Send(MULTIDIM_ARRAY(Msum), MULTIDIM_SIZE(Msum), MPI_DOUBLE, other_slave, MPITAG_PACK, MPI_COMM_WORLD);
+						node->relion_MPI_Send(MULTIDIM_ARRAY(Msum), MULTIDIM_SIZE(Msum), MY_MPI_DOUBLE, other_slave, MPITAG_PACK, MPI_COMM_WORLD);
 					}
 					else if (node->rank == other_slave)
 					{
-						node->relion_MPI_Recv(MULTIDIM_ARRAY(Msum), MULTIDIM_SIZE(Msum), MPI_DOUBLE, this_slave, MPITAG_PACK, MPI_COMM_WORLD, status);
+						node->relion_MPI_Recv(MULTIDIM_ARRAY(Msum), MULTIDIM_SIZE(Msum), MY_MPI_DOUBLE, this_slave, MPITAG_PACK, MPI_COMM_WORLD, status);
 #ifdef DEBUG
 						std::cerr << " CC RECV node->rank= " << node->rank << " MULTIDIM_SIZE(Msum)= "<< MULTIDIM_SIZE(Msum)
 								<< " this_slave= " << this_slave << " other_slave= "<<other_slave << std::endl;
@@ -907,6 +1046,9 @@ void MlOptimiserMpi::combineAllWeightedSums()
 		MPI_Barrier(MPI_COMM_WORLD);
 	}
 
+#ifdef TIMING
+    timer.toc(TIMING_MPICOMBINENETW);
+#endif
 
 }
 
@@ -916,10 +1058,10 @@ void MlOptimiserMpi::combineWeightedSumsTwoRandomHalvesViaFile()
 	if (!do_split_random_halves)
 		REPORT_ERROR("MlOptimiserMpi::combineWeightedSumsTwoRandomHalvesViaFile BUG: you cannot combineWeightedSumsTwoRandomHalves if you have not split random halves");
 
-	MultidimArray<double> Mpack;
+	MultidimArray<DOUBLE> Mpack;
 	FileName fn_pack = fn_out + ".tmp";
-	if (fn_scratch != "")
-		fn_pack = fn_scratch + "/" + fn_pack;
+        if (fn_scratch != "")
+            fn_pack = fn_scratch + "/" + fn_pack;
 
 	// Everyone packs up his wsum_model (simultaneously)
 	// The slaves from 3 and onwards also need this in order to have the correct Mpack size to be able to read in the summed Mpack
@@ -949,9 +1091,13 @@ void MlOptimiserMpi::combineWeightedSumsTwoRandomHalvesViaFile()
 	{
 		if (node->rank == this_slave)
 			Mpack.readBinary(fn_pack);
-		MPI_Barrier(MPI_COMM_WORLD);
+		if (!do_parallel_disc_io)
+			MPI_Barrier(MPI_COMM_WORLD);
 	}
 
+	// in case we're doing do_parallel_disc_io
+	MPI_Barrier(MPI_COMM_WORLD);
+
 	// Remove temporary file
 	if (node->rank == 1)
 		remove(fn_pack.c_str());
@@ -968,7 +1114,7 @@ void MlOptimiserMpi::combineWeightedSumsTwoRandomHalves()
 	if (!do_split_random_halves)
 		REPORT_ERROR("MlOptimiserMpi::combineWeightedSumsTwoRandomHalves BUG: you cannot combineWeightedSumsTwoRandomHalves if you have not split random halves");
 
-	MultidimArray<double> Mpack, Msum;
+	MultidimArray<DOUBLE> Mpack, Msum;
 	MPI_Status status;
 
 	int piece = 0;
@@ -982,7 +1128,7 @@ void MlOptimiserMpi::combineWeightedSumsTwoRandomHalves()
 		if (node->rank == 2)
 		{
 			wsum_model.pack(Mpack, piece, nr_pieces, false); // do not clear the model!
-			node->relion_MPI_Send(MULTIDIM_ARRAY(Mpack), MULTIDIM_SIZE(Mpack), MPI_DOUBLE, 1, MPITAG_PACK, MPI_COMM_WORLD);
+			node->relion_MPI_Send(MULTIDIM_ARRAY(Mpack), MULTIDIM_SIZE(Mpack), MY_MPI_DOUBLE, 1, MPITAG_PACK, MPI_COMM_WORLD);
 			Mpack.clear();
 		}
 		else if (node->rank == 1)
@@ -990,7 +1136,7 @@ void MlOptimiserMpi::combineWeightedSumsTwoRandomHalves()
 			std::cout << " Combining two random halves ..."<< std::endl;
 			wsum_model.pack(Mpack, piece, nr_pieces);
 			Msum.initZeros(Mpack);
-			node->relion_MPI_Recv(MULTIDIM_ARRAY(Msum), MULTIDIM_SIZE(Msum), MPI_DOUBLE, 2, MPITAG_PACK, MPI_COMM_WORLD, status);
+			node->relion_MPI_Recv(MULTIDIM_ARRAY(Msum), MULTIDIM_SIZE(Msum), MY_MPI_DOUBLE, 2, MPITAG_PACK, MPI_COMM_WORLD, status);
 			Msum += Mpack;
 			// Unpack the sum (subtract 1 from piece because it was incremented already...)
 			wsum_model.unpack(Msum, piece - 1);
@@ -1021,11 +1167,11 @@ void MlOptimiserMpi::combineWeightedSumsTwoRandomHalves()
 			if (node->rank == 1)
 			{
 				for (int other_slave = 2; other_slave < node->size; other_slave++)
-					node->relion_MPI_Send(MULTIDIM_ARRAY(Mpack), MULTIDIM_SIZE(Mpack), MPI_DOUBLE, other_slave, MPITAG_PACK, MPI_COMM_WORLD);
+					node->relion_MPI_Send(MULTIDIM_ARRAY(Mpack), MULTIDIM_SIZE(Mpack), MY_MPI_DOUBLE, other_slave, MPITAG_PACK, MPI_COMM_WORLD);
 			}
 			else
 			{
-				node->relion_MPI_Recv(MULTIDIM_ARRAY(Mpack), MULTIDIM_SIZE(Mpack), MPI_DOUBLE, 1, MPITAG_PACK, MPI_COMM_WORLD, status);
+				node->relion_MPI_Recv(MULTIDIM_ARRAY(Mpack), MULTIDIM_SIZE(Mpack), MY_MPI_DOUBLE, 1, MPITAG_PACK, MPI_COMM_WORLD, status);
 			}
 
 			// Everyone unpacks the new Mpack
@@ -1151,18 +1297,18 @@ void MlOptimiserMpi::maximization()
 #ifdef DEBUG
 							std::cerr << "isubset= "<<isubset<<" Sending iclass="<<iclass<<" from node "<<reconstruct_rank<<" to node "<<recv_node << std::endl;
 #endif
-							node->relion_MPI_Send(MULTIDIM_ARRAY(mymodel.Iref[iclass]), MULTIDIM_SIZE(mymodel.Iref[iclass]), MPI_DOUBLE, recv_node, MPITAG_IMAGE, MPI_COMM_WORLD);
-							node->relion_MPI_Send(MULTIDIM_ARRAY(mymodel.data_vs_prior_class[iclass]), MULTIDIM_SIZE(mymodel.data_vs_prior_class[iclass]), MPI_DOUBLE, recv_node, MPITAG_METADATA, MPI_COMM_WORLD);
-							node->relion_MPI_Send(MULTIDIM_ARRAY(mymodel.sigma2_class[iclass]), MULTIDIM_SIZE(mymodel.sigma2_class[iclass]), MPI_DOUBLE, recv_node, MPITAG_DOUBLE, MPI_COMM_WORLD);
-							node->relion_MPI_Send(MULTIDIM_ARRAY(mymodel.fsc_halves_class[iclass]), MULTIDIM_SIZE(mymodel.fsc_halves_class[iclass]), MPI_DOUBLE, recv_node, MPITAG_RANDOMSEED, MPI_COMM_WORLD);
+							node->relion_MPI_Send(MULTIDIM_ARRAY(mymodel.Iref[iclass]), MULTIDIM_SIZE(mymodel.Iref[iclass]), MY_MPI_DOUBLE, recv_node, MPITAG_IMAGE, MPI_COMM_WORLD);
+							node->relion_MPI_Send(MULTIDIM_ARRAY(mymodel.data_vs_prior_class[iclass]), MULTIDIM_SIZE(mymodel.data_vs_prior_class[iclass]), MY_MPI_DOUBLE, recv_node, MPITAG_METADATA, MPI_COMM_WORLD);
+							node->relion_MPI_Send(MULTIDIM_ARRAY(mymodel.sigma2_class[iclass]), MULTIDIM_SIZE(mymodel.sigma2_class[iclass]), MY_MPI_DOUBLE, recv_node, MPITAG_DOUBLE, MPI_COMM_WORLD);
+							node->relion_MPI_Send(MULTIDIM_ARRAY(mymodel.fsc_halves_class[iclass]), MULTIDIM_SIZE(mymodel.fsc_halves_class[iclass]), MY_MPI_DOUBLE, recv_node, MPITAG_RANDOMSEED, MPI_COMM_WORLD);
 						}
 						else if (node->rank != reconstruct_rank && node->rank == recv_node)
 						{
 							//std::cerr << "isubset= "<<isubset<< " Receiving iclass="<<iclass<<" from node "<<reconstruct_rank<<" at node "<<node->rank<< std::endl;
-							node->relion_MPI_Recv(MULTIDIM_ARRAY(mymodel.Iref[iclass]), MULTIDIM_SIZE(mymodel.Iref[iclass]), MPI_DOUBLE, reconstruct_rank, MPITAG_IMAGE, MPI_COMM_WORLD, status);
-							node->relion_MPI_Recv(MULTIDIM_ARRAY(mymodel.data_vs_prior_class[iclass]), MULTIDIM_SIZE(mymodel.data_vs_prior_class[iclass]), MPI_DOUBLE, reconstruct_rank, MPITAG_METADATA, MPI_COMM_WORLD, status);
-							node->relion_MPI_Recv(MULTIDIM_ARRAY(mymodel.sigma2_class[iclass]), MULTIDIM_SIZE(mymodel.sigma2_class[iclass]), MPI_DOUBLE, reconstruct_rank, MPITAG_DOUBLE, MPI_COMM_WORLD, status);
-							node->relion_MPI_Recv(MULTIDIM_ARRAY(mymodel.fsc_halves_class[iclass]), MULTIDIM_SIZE(mymodel.fsc_halves_class[iclass]), MPI_DOUBLE, reconstruct_rank, MPITAG_RANDOMSEED, MPI_COMM_WORLD, status);
+							node->relion_MPI_Recv(MULTIDIM_ARRAY(mymodel.Iref[iclass]), MULTIDIM_SIZE(mymodel.Iref[iclass]), MY_MPI_DOUBLE, reconstruct_rank, MPITAG_IMAGE, MPI_COMM_WORLD, status);
+							node->relion_MPI_Recv(MULTIDIM_ARRAY(mymodel.data_vs_prior_class[iclass]), MULTIDIM_SIZE(mymodel.data_vs_prior_class[iclass]), MY_MPI_DOUBLE, reconstruct_rank, MPITAG_METADATA, MPI_COMM_WORLD, status);
+							node->relion_MPI_Recv(MULTIDIM_ARRAY(mymodel.sigma2_class[iclass]), MULTIDIM_SIZE(mymodel.sigma2_class[iclass]), MY_MPI_DOUBLE, reconstruct_rank, MPITAG_DOUBLE, MPI_COMM_WORLD, status);
+							node->relion_MPI_Recv(MULTIDIM_ARRAY(mymodel.fsc_halves_class[iclass]), MULTIDIM_SIZE(mymodel.fsc_halves_class[iclass]), MY_MPI_DOUBLE, reconstruct_rank, MPITAG_RANDOMSEED, MPI_COMM_WORLD, status);
 #ifdef DEBUG
 							std::cerr << "isubset= "<<isubset<< " Received!!!="<<iclass<<" from node "<<reconstruct_rank<<" at node "<<node->rank<< std::endl;
 #endif
@@ -1183,13 +1329,13 @@ void MlOptimiserMpi::maximization()
 			int reconstruct_rank = (iclass % (node->size - 1) ) + 1;
 			// Broadcast the reconstructed references to all other MPI nodes
 			node->relion_MPI_Bcast(MULTIDIM_ARRAY(mymodel.Iref[iclass]),
-					MULTIDIM_SIZE(mymodel.Iref[iclass]), MPI_DOUBLE, reconstruct_rank, MPI_COMM_WORLD);
+					MULTIDIM_SIZE(mymodel.Iref[iclass]), MY_MPI_DOUBLE, reconstruct_rank, MPI_COMM_WORLD);
 			// Broadcast the data_vs_prior spectra to all other MPI nodes
 			node->relion_MPI_Bcast(MULTIDIM_ARRAY(mymodel.data_vs_prior_class[iclass]),
-					MULTIDIM_SIZE(mymodel.data_vs_prior_class[iclass]), MPI_DOUBLE, reconstruct_rank, MPI_COMM_WORLD);
+					MULTIDIM_SIZE(mymodel.data_vs_prior_class[iclass]), MY_MPI_DOUBLE, reconstruct_rank, MPI_COMM_WORLD);
 			// Broadcast the sigma2_class spectra to all other MPI nodes
 			node->relion_MPI_Bcast(MULTIDIM_ARRAY(mymodel.sigma2_class[iclass]),
-					MULTIDIM_SIZE(mymodel.sigma2_class[iclass]), MPI_DOUBLE, reconstruct_rank, MPI_COMM_WORLD);
+					MULTIDIM_SIZE(mymodel.sigma2_class[iclass]), MY_MPI_DOUBLE, reconstruct_rank, MPI_COMM_WORLD);
 
 		}
 
@@ -1219,13 +1365,13 @@ void MlOptimiserMpi::maximization()
 	}
 
 	// The master broadcasts the changes in hidden variables to all other nodes
-	node->relion_MPI_Bcast(&current_changes_optimal_classes, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
-	node->relion_MPI_Bcast(&current_changes_optimal_orientations, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
-	node->relion_MPI_Bcast(&current_changes_optimal_offsets, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+	node->relion_MPI_Bcast(&current_changes_optimal_classes, 1, MY_MPI_DOUBLE, 0, MPI_COMM_WORLD);
+	node->relion_MPI_Bcast(&current_changes_optimal_orientations, 1, MY_MPI_DOUBLE, 0, MPI_COMM_WORLD);
+	node->relion_MPI_Bcast(&current_changes_optimal_offsets, 1, MY_MPI_DOUBLE, 0, MPI_COMM_WORLD);
 	node->relion_MPI_Bcast(&nr_iter_wo_large_hidden_variable_changes, 1, MPI_INT, 0, MPI_COMM_WORLD);
-	node->relion_MPI_Bcast(&smallest_changes_optimal_classes, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
-	node->relion_MPI_Bcast(&smallest_changes_optimal_offsets, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
-	node->relion_MPI_Bcast(&smallest_changes_optimal_orientations, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+	node->relion_MPI_Bcast(&smallest_changes_optimal_classes, 1, MY_MPI_DOUBLE, 0, MPI_COMM_WORLD);
+	node->relion_MPI_Bcast(&smallest_changes_optimal_offsets, 1, MY_MPI_DOUBLE, 0, MPI_COMM_WORLD);
+	node->relion_MPI_Bcast(&smallest_changes_optimal_orientations, 1, MY_MPI_DOUBLE, 0, MPI_COMM_WORLD);
 
 	if (verb > 0)
 		progress_bar(mymodel.nr_classes);
@@ -1246,7 +1392,7 @@ void MlOptimiserMpi::joinTwoHalvesAtLowResolution()
 		REPORT_ERROR("BUG: you should not be in MlOptimiserMpi::joinTwoHalvesAtLowResolution!");
 
 	// Loop over all classes (this will be just one class for now...)
-	double myres = XMIPP_MAX(low_resol_join_halves, 1./mymodel.current_resolution);
+	DOUBLE myres = XMIPP_MAX(low_resol_join_halves, 1./mymodel.current_resolution);
 	int lowres_r_max = CEIL(mymodel.ori_size * mymodel.pixel_size / myres);
 
 	for (int iclass = 0; iclass < mymodel.nr_classes; iclass++ )
@@ -1254,7 +1400,7 @@ void MlOptimiserMpi::joinTwoHalvesAtLowResolution()
 		if (node->rank == 1 || node->rank == 2)
 		{
 			MultidimArray<Complex > lowres_data;
-			MultidimArray<double > lowres_weight;
+			MultidimArray<DOUBLE > lowres_weight;
 			wsum_model.BPref[iclass].getLowResDataAndWeight(lowres_data, lowres_weight, lowres_r_max);
 
 			if (node->rank == 2)
@@ -1262,14 +1408,14 @@ void MlOptimiserMpi::joinTwoHalvesAtLowResolution()
 				MPI_Status status;
 
 				// The second slave sends its lowres_data and lowres_weight to the first slave
-				node->relion_MPI_Send(MULTIDIM_ARRAY(lowres_data), 2*MULTIDIM_SIZE(lowres_data), MPI_DOUBLE, 1, MPITAG_IMAGE, MPI_COMM_WORLD);
-				node->relion_MPI_Send(MULTIDIM_ARRAY(lowres_weight), MULTIDIM_SIZE(lowres_weight), MPI_DOUBLE, 1, MPITAG_DOUBLE, MPI_COMM_WORLD);
+				node->relion_MPI_Send(MULTIDIM_ARRAY(lowres_data), 2*MULTIDIM_SIZE(lowres_data), MY_MPI_DOUBLE, 1, MPITAG_IMAGE, MPI_COMM_WORLD);
+				node->relion_MPI_Send(MULTIDIM_ARRAY(lowres_weight), MULTIDIM_SIZE(lowres_weight), MY_MPI_DOUBLE, 1, MPITAG_DOUBLE, MPI_COMM_WORLD);
 
 				// Now the first slave is calculating the average....
 
 				// Then the second slave receives the average back from the first slave
-				node->relion_MPI_Recv(MULTIDIM_ARRAY(lowres_data), 2*MULTIDIM_SIZE(lowres_data), MPI_DOUBLE, 1, MPITAG_IMAGE, MPI_COMM_WORLD, status);
-				node->relion_MPI_Recv(MULTIDIM_ARRAY(lowres_weight), MULTIDIM_SIZE(lowres_weight), MPI_DOUBLE, 1, MPITAG_DOUBLE, MPI_COMM_WORLD, status);
+				node->relion_MPI_Recv(MULTIDIM_ARRAY(lowres_data), 2*MULTIDIM_SIZE(lowres_data), MY_MPI_DOUBLE, 1, MPITAG_IMAGE, MPI_COMM_WORLD, status);
+				node->relion_MPI_Recv(MULTIDIM_ARRAY(lowres_weight), MULTIDIM_SIZE(lowres_weight), MY_MPI_DOUBLE, 1, MPITAG_DOUBLE, MPI_COMM_WORLD, status);
 
 
 			}
@@ -1280,7 +1426,7 @@ void MlOptimiserMpi::joinTwoHalvesAtLowResolution()
 				std::cout << " Note that only for higher resolutions the FSC-values are according to the gold-standard!" << std::endl;
 				MPI_Status status;
 				MultidimArray<Complex > lowres_data_half2;
-				MultidimArray<double > lowres_weight_half2;
+				MultidimArray<DOUBLE > lowres_weight_half2;
 				lowres_data_half2.resize(lowres_data);
 				lowres_weight_half2.resize(lowres_weight);
 #ifdef DEBUG
@@ -1288,8 +1434,8 @@ void MlOptimiserMpi::joinTwoHalvesAtLowResolution()
 				std::cerr << "AAArank=1 lowresdata_half2: "; lowres_data_half2.printShape();
 #endif
 				// The first slave receives the average from the second slave
-				node->relion_MPI_Recv(MULTIDIM_ARRAY(lowres_data_half2), 2*MULTIDIM_SIZE(lowres_data_half2), MPI_DOUBLE, 2, MPITAG_IMAGE, MPI_COMM_WORLD, status);
-				node->relion_MPI_Recv(MULTIDIM_ARRAY(lowres_weight_half2), MULTIDIM_SIZE(lowres_weight_half2), MPI_DOUBLE, 2, MPITAG_DOUBLE, MPI_COMM_WORLD, status);
+				node->relion_MPI_Recv(MULTIDIM_ARRAY(lowres_data_half2), 2*MULTIDIM_SIZE(lowres_data_half2), MY_MPI_DOUBLE, 2, MPITAG_IMAGE, MPI_COMM_WORLD, status);
+				node->relion_MPI_Recv(MULTIDIM_ARRAY(lowres_weight_half2), MULTIDIM_SIZE(lowres_weight_half2), MY_MPI_DOUBLE, 2, MPITAG_DOUBLE, MPI_COMM_WORLD, status);
 
 				// The first slave calculates the average of the two lowres_data and lowres_weight arrays
 #ifdef DEBUG
@@ -1305,8 +1451,8 @@ void MlOptimiserMpi::joinTwoHalvesAtLowResolution()
 				}
 
 				// The first slave sends the average lowres_data and lowres_weight also back to the second slave
-				node->relion_MPI_Send(MULTIDIM_ARRAY(lowres_data), 2*MULTIDIM_SIZE(lowres_data), MPI_DOUBLE, 2, MPITAG_IMAGE, MPI_COMM_WORLD);
-				node->relion_MPI_Send(MULTIDIM_ARRAY(lowres_weight), MULTIDIM_SIZE(lowres_weight), MPI_DOUBLE, 2, MPITAG_DOUBLE, MPI_COMM_WORLD);
+				node->relion_MPI_Send(MULTIDIM_ARRAY(lowres_data), 2*MULTIDIM_SIZE(lowres_data), MY_MPI_DOUBLE, 2, MPITAG_IMAGE, MPI_COMM_WORLD);
+				node->relion_MPI_Send(MULTIDIM_ARRAY(lowres_weight), MULTIDIM_SIZE(lowres_weight), MY_MPI_DOUBLE, 2, MPITAG_DOUBLE, MPI_COMM_WORLD);
 
 			}
 
@@ -1327,7 +1473,7 @@ void MlOptimiserMpi::writeTemporaryDataAndWeightArrays()
 
 	if (mymodel.ref_dim == 3 && (node->rank == 1 || (do_split_random_halves && node->rank == 2) ) )
 	{
-		Image<double> It;
+		Image<DOUBLE> It;
 		FileName fn_root = fn_out + "_half" + integerToString(node->rank);;
 
 		// Write out temporary arrays for all classes
@@ -1360,8 +1506,8 @@ void MlOptimiserMpi::readTemporaryDataAndWeightArraysAndReconstruct(int iclass,
 {
 	if (mymodel.ref_dim == 3)
 	{
-		MultidimArray<double> dummy;
-		Image<double> Iunreg, Itmp;
+		MultidimArray<DOUBLE> dummy;
+		Image<DOUBLE> Iunreg, Itmp;
 		FileName fn_root = fn_out + "_half" + integerToString(ihalf);;
 		fn_root.compose(fn_root+"_class", iclass+1, "", 3);
 
@@ -1412,7 +1558,7 @@ void MlOptimiserMpi::readTemporaryDataAndWeightArraysAndReconstruct(int iclass,
 		wsum_model.BPref[iclass].reconstruct(Iunreg(), gridding_nr_iter, false, 1., dummy, dummy, dummy, dummy, 1., false, true, nr_threads, -1);
 
 		// Update header information
-		double avg, stddev, minval, maxval;
+		DOUBLE avg, stddev, minval, maxval;
 	    Iunreg().computeStats(avg, stddev, minval, maxval);
 	    Iunreg.MDMainHeader.setValue(EMDL_IMAGE_STATS_MIN, minval);
 	    Iunreg.MDMainHeader.setValue(EMDL_IMAGE_STATS_MAX, maxval);
@@ -1453,7 +1599,7 @@ void MlOptimiserMpi::compareTwoHalves()
 //#define DEBUG_FSC
 #ifdef DEBUG_FSC
 			MultidimArray<Complex > avg;
-			MultidimArray<double> Mavg;
+			MultidimArray<DOUBLE> Mavg;
 			Mavg.resize(mymodel.ori_size, mymodel.ori_size, mymodel.ori_size);
 			FourierTransformer transformer_debug;
 			transformer_debug.setReal(Mavg);
@@ -1462,7 +1608,7 @@ void MlOptimiserMpi::compareTwoHalves()
 			transformer_debug.inverseFourierTransform();
 			FileName fnt;
 			fnt.compose("downsampled_avg_half",node->rank,"spi");
-			Image<double> It;
+			Image<DOUBLE> It;
 			CenterFFT(Mavg, true);
 			It()=Mavg;
 			It.write(fnt);
@@ -1470,7 +1616,7 @@ void MlOptimiserMpi::compareTwoHalves()
 			if (node->rank == 2)
 			{
 				// The second slave sends its average to the first slave
-				node->relion_MPI_Send(MULTIDIM_ARRAY(avg1), 2*MULTIDIM_SIZE(avg1), MPI_DOUBLE, 1, MPITAG_IMAGE, MPI_COMM_WORLD);
+				node->relion_MPI_Send(MULTIDIM_ARRAY(avg1), 2*MULTIDIM_SIZE(avg1), MY_MPI_DOUBLE, 1, MPITAG_IMAGE, MPI_COMM_WORLD);
 			}
 			else if (node->rank == 1)
 			{
@@ -1480,14 +1626,14 @@ void MlOptimiserMpi::compareTwoHalves()
 				MPI_Status status;
 				MultidimArray<Complex > avg2;
 				avg2.resize(avg1);
-				node->relion_MPI_Recv(MULTIDIM_ARRAY(avg2), 2*MULTIDIM_SIZE(avg2), MPI_DOUBLE, 2, MPITAG_IMAGE, MPI_COMM_WORLD, status);
+				node->relion_MPI_Recv(MULTIDIM_ARRAY(avg2), 2*MULTIDIM_SIZE(avg2), MY_MPI_DOUBLE, 2, MPITAG_IMAGE, MPI_COMM_WORLD, status);
 				wsum_model.BPref[iclass].calculateDownSampledFourierShellCorrelation(avg1, avg2, mymodel.fsc_halves_class[iclass]);
 			}
 
 		}
 
 		// Now slave 1 sends the fsc curve to everyone else
-		node->relion_MPI_Bcast(MULTIDIM_ARRAY(mymodel.fsc_halves_class[iclass]), MULTIDIM_SIZE(mymodel.fsc_halves_class[iclass]), MPI_DOUBLE, 1, MPI_COMM_WORLD);
+		node->relion_MPI_Bcast(MULTIDIM_ARRAY(mymodel.fsc_halves_class[iclass]), MULTIDIM_SIZE(mymodel.fsc_halves_class[iclass]), MY_MPI_DOUBLE, 1, MPI_COMM_WORLD);
 	}
 
 #ifdef DEBUG
@@ -1500,6 +1646,12 @@ void MlOptimiserMpi::iterate()
 #ifdef TIMING
 	// MPI-specific timing stuff goes here...
 	TIMING_MPIWAIT= timer.setNew("mpiWaitEndOfExpectation");
+	TIMING_MPICOMBINEDISC= timer.setNew("mpiCombineThroughDisc");
+	TIMING_MPICOMBINENETW= timer.setNew("mpiCombineThroughNetwork");
+	TIMING_MPISLAVEWORK= timer.setNew("mpiSlaveWorking");
+	TIMING_MPISLAVEWAIT1= timer.setNew("mpiSlaveWaiting1");
+	TIMING_MPISLAVEWAIT2= timer.setNew("mpiSlaveWaiting2");
+	TIMING_MPISLAVEWAIT3= timer.setNew("mpiSlaveWaiting3");
 #endif
 
 
@@ -1602,7 +1754,7 @@ void MlOptimiserMpi::iterate()
 		// Make sure all nodes have the same resolution, set the data_vs_prior array from half1 also for half2
 		if (do_split_random_halves)
 			for (int iclass = 0; iclass < mymodel.nr_classes; iclass++)
-				node->relion_MPI_Bcast(MULTIDIM_ARRAY(mymodel.data_vs_prior_class[iclass]), MULTIDIM_SIZE(mymodel.data_vs_prior_class[iclass]), MPI_DOUBLE, 1, MPI_COMM_WORLD);
+				node->relion_MPI_Bcast(MULTIDIM_ARRAY(mymodel.data_vs_prior_class[iclass]), MULTIDIM_SIZE(mymodel.data_vs_prior_class[iclass]), MY_MPI_DOUBLE, 1, MPI_COMM_WORLD);
 
 #ifdef TIMING
 		timer.toc(TIMING_MAX);
@@ -1666,7 +1818,7 @@ void MlOptimiserMpi::iterate()
 
 #ifdef TIMING
 		// Only first slave prints it timing information
-		else if (node->rank == 1)
+		if (node->rank == 1)
 			timer.printTimes(false);
 #endif
 
diff --git a/src/ml_optimiser_mpi.h b/src/ml_optimiser_mpi.h
index 8cc3184..9e1aba0 100644
--- a/src/ml_optimiser_mpi.h
+++ b/src/ml_optimiser_mpi.h
@@ -33,7 +33,8 @@
 #define MPITAG_INT 7
 
 #ifdef TIMING
-	int TIMING_MPIPACK, TIMING_MPIWAIT;
+	int TIMING_MPIPACK, TIMING_MPIWAIT, TIMING_MPICOMBINEDISC, TIMING_MPICOMBINENETW, TIMING_MPISLAVEWORK;
+	int TIMING_MPISLAVEWAIT1, TIMING_MPISLAVEWAIT2, TIMING_MPISLAVEWAIT3;
 #endif
 
 class MlOptimiserMpi: public MlOptimiser
@@ -75,7 +76,7 @@ public:
     void initialiseWorkLoad();
 
     /** Perform individual power spectra calculation in parallel */
-    void calculateSumOfPowerSpectraAndAverageImage(MultidimArray<double> &Mavg);
+    void calculateSumOfPowerSpectraAndAverageImage(MultidimArray<DOUBLE> &Mavg);
 
     /** Expectation
      *  This cares care of gathering all weighted sums after the expectation
diff --git a/src/mpi.cpp b/src/mpi.cpp
index a197fd6..03b32ce 100644
--- a/src/mpi.cpp
+++ b/src/mpi.cpp
@@ -81,93 +81,155 @@ void MpiNode::barrierWait()
 
 // MPI_TEST will be executed every this many seconds: so this determines the minimum time taken for every send operation!!
 //#define VERBOSE_MPISENDRECV
-int MpiNode::relion_MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
-{
-
-	int result;
-	double start_time = MPI_Wtime();
-
-#define ONLY_NORMAL_SEND
-#ifdef ONLY_NORMAL_SEND
-	result = MPI_Send(buf, count, datatype, dest, tag, comm);
-	if (result != MPI_SUCCESS)
-	{
-		report_MPI_ERROR(result);
-	}
+int MpiNode::relion_MPI_Send(void *buf, std::ptrdiff_t count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm) {
+
+    int result(0);
+    DOUBLE start_time = MPI_Wtime();
+
+//#define ONLY_NORMAL_SEND
+//#ifdef ONLY_NORMAL_SEND
+    int unitsize(0);
+    MPI_Type_size(datatype, &unitsize);
+    const std::ptrdiff_t blocksize(512*1024*1024);
+    const std::ptrdiff_t totalsize(count*unitsize);
+    if (totalsize <= blocksize ) {
+        result = MPI_Send(buf, count, datatype, dest, tag, comm);
+        if (result != MPI_SUCCESS) {
+            report_MPI_ERROR(result);
+        }
+    } else {
+        char * const buffer(reinterpret_cast<char*>(buf));
+        const std::ptrdiff_t ntimes(totalsize/blocksize);
+        const std::ptrdiff_t nremain(totalsize%blocksize);
+        std::ptrdiff_t i(0);
+        for(; i<ntimes; ++i) {
+            result = MPI_Send(buffer+i*blocksize, blocksize, MPI_CHAR, dest, tag, comm);
+            if (result != MPI_SUCCESS) {
+                report_MPI_ERROR(result);
+            }
+        }
+        if(nremain>0) {
+            result = MPI_Send(buffer+i*blocksize, nremain, MPI_CHAR, dest, tag, comm);
+            if (result != MPI_SUCCESS) {
+                report_MPI_ERROR(result);
+            }
+        }
+    }
+/*
 #else
-	// Only use Bsend for larger messages, otherwise use normal send
-	if (count > 100)
-	{
-		int size;
-		MPI_Pack_size( count, datatype, comm, &size );
-		char *membuff;
-
-		// Allocate memory for the package to be sent
-		int attach_result = MPI_Buffer_attach( malloc(size + MPI_BSEND_OVERHEAD ), size + MPI_BSEND_OVERHEAD );
-		if (attach_result != MPI_SUCCESS)
-		{
-			report_MPI_ERROR(result);
-		}
-
-		// Actually start sending the message
-		result = MPI_Bsend(buf, count, datatype, dest, tag, comm);
-		if (result != MPI_SUCCESS)
-		{
-			report_MPI_ERROR(result);
-		}
-
-		// The following will only complete once the message has been successfully sent (i.e. also received on the other side)
-		int deattach_result = MPI_Buffer_detach( &membuff, &size);
-		if (deattach_result != MPI_SUCCESS)
-		{
-			report_MPI_ERROR(result);
-		}
-	}
-	else
-	{
-		result = MPI_Send(buf, count, datatype, dest, tag, comm);
-		if (result != MPI_SUCCESS)
-		{
-			report_MPI_ERROR(result);
-		}
-	}
+        // Only use Bsend for larger messages, otherwise use normal send
+        if (count > 100) {
+                int size;
+                MPI_Pack_size( count, datatype, comm, &size );
+                char *membuff;
+
+                // Allocate memory for the package to be sent
+                int attach_result = MPI_Buffer_attach( malloc(size + MPI_BSEND_OVERHEAD ), size + MPI_BSEND_OVERHEAD );
+                if (attach_result != MPI_SUCCESS)
+                {
+                        report_MPI_ERROR(result);
+                }
+
+                // Actually start sending the message
+                result = MPI_Bsend(buf, count, datatype, dest, tag, comm);
+                if (result != MPI_SUCCESS)
+                {
+                        report_MPI_ERROR(result);
+                }
+
+                // The following will only complete once the message has been successfully sent (i.e. also received on the other side)
+                int deattach_result = MPI_Buffer_detach( &membuff, &size);
+                if (deattach_result != MPI_SUCCESS)
+                {
+                        report_MPI_ERROR(result);
+                }
+        } else {
+                result = MPI_Send(buf, count, datatype, dest, tag, comm);
+                if (result != MPI_SUCCESS)
+                {
+                        report_MPI_ERROR(result);
+                }
+        }
 #endif
+*/
 
 #ifdef VERBOSE_MPISENDRECV
-	if (count > 100)
-		std::cerr <<" relion_MPI_Send: message to " << dest << " of size "<< count << " arrived in " << MPI_Wtime() - start_time << " seconds" << std::endl;
+        if (count > 100)
+                std::cerr <<" relion_MPI_Send: message to " << dest << " of size "<< count << " arrived in " << MPI_Wtime() - start_time << " seconds" << std::endl;
 #endif
-	return result;
+        return result;
 
 }
 
-int MpiNode::relion_MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status &status)
-{
-	int result;
-	MPI_Request request;
-	double current_time = MPI_Wtime();
-	double start_time = current_time;
-
-	// First make a non-blocking receive
-	int result_irecv = MPI_Irecv(buf, count, datatype, source, tag, comm, &request);
-	if (result_irecv != MPI_SUCCESS)
-	{
-		report_MPI_ERROR(result_irecv);
-	}
-
-	// I could do something in between. If not, Irecv == Recv
-	// Wait for it to finish (MPI_Irecv + MPI_Wait == MPI_Recv)
-	result = MPI_Wait(&request, &status);
-	if (result != MPI_SUCCESS)
-	{
-		report_MPI_ERROR(result);
-	}
-
+int MpiNode::relion_MPI_Recv(void *buf, std::ptrdiff_t count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status &status) {
+    int result;
+    MPI_Request request;
+    DOUBLE current_time = MPI_Wtime();
+    DOUBLE start_time = current_time;
+
+    int unitsize(0);
+    MPI_Type_size(datatype, &unitsize);
+    const std::ptrdiff_t blocksize(512*1024*1024);
+    const std::ptrdiff_t totalsize(count*unitsize);
+    if (totalsize <= blocksize ) {
+        int result_irecv = MPI_Irecv(buf, count, datatype, source, tag, comm, &request);
+        if (result_irecv != MPI_SUCCESS) {
+            report_MPI_ERROR(result_irecv);
+        }
+
+        result = MPI_Wait(&request, &status);
+        if (result != MPI_SUCCESS) {
+            report_MPI_ERROR(result);
+        }
+    } else {
+        char * const buffer(reinterpret_cast<char*>(buf));
+        const std::ptrdiff_t ntimes(totalsize/blocksize);
+        const std::ptrdiff_t nremain(totalsize%blocksize);
+        std::ptrdiff_t i(0);
+        for(; i<ntimes; ++i) {
+            int result_irecv = MPI_Irecv(buffer+i*blocksize, blocksize, MPI_CHAR, source, tag, comm, &request);
+            if (result_irecv != MPI_SUCCESS) {
+                report_MPI_ERROR(result_irecv);
+            }
+
+            result = MPI_Wait(&request, &status);
+            if (result != MPI_SUCCESS) {
+                report_MPI_ERROR(result);
+            }
+        }
+        if(nremain>0) {
+            int result_irecv = MPI_Irecv(buffer+i*blocksize, nremain, MPI_CHAR, source, tag, comm, &request);
+            if (result_irecv != MPI_SUCCESS) {
+                report_MPI_ERROR(result_irecv);
+            }
+
+            result = MPI_Wait(&request, &status);
+            if (result != MPI_SUCCESS) {
+                report_MPI_ERROR(result);
+            }
+        }
+    }
+/*
+        // First make a non-blocking receive
+        int result_irecv = MPI_Irecv(buf, count, datatype, source, tag, comm, &request);
+        if (result_irecv != MPI_SUCCESS)
+        {
+                report_MPI_ERROR(result_irecv);
+        }
+
+        // I could do something in between. If not, Irecv == Recv
+        // Wait for it to finish (MPI_Irecv + MPI_Wait == MPI_Recv)
+        result = MPI_Wait(&request, &status);
+        if (result != MPI_SUCCESS)
+        {
+                report_MPI_ERROR(result);
+        }
+*/
 #ifdef VERBOSE_MPISENDRECV
-	if (count > 100)
-		std::cerr <<" relion_MPI_Recv: message from "<<source << " of size "<< count <<" arrived in " << MPI_Wtime() - start_time << " seconds" << std::endl;
+        if (count > 100)
+                std::cerr <<" relion_MPI_Recv: message from "<<source << " of size "<< count <<" arrived in " << MPI_Wtime() - start_time << " seconds" << std::endl;
 #endif
-	return result;
+        return result;
 
 }
 
diff --git a/src/mpi.h b/src/mpi.h
index 1efd3bb..45fa3d1 100644
--- a/src/mpi.h
+++ b/src/mpi.h
@@ -45,10 +45,12 @@
 #ifndef MPI_H_
 #define MPI_H_
 #include <mpi.h>
-#include <stdlib.h>
-#include <stdio.h>
+#include <cstddef>
+#include <cstdlib>
+#include <cstdio>
 #include <unistd.h>
 #include "src/error.h"
+#include "src/macros.h"
 
  /** Class to wrapp some MPI common calls in an work node.
  *
@@ -73,9 +75,9 @@ public:
 
     /** Build in some better error handling and (hopefully better) robustness to communication failures in the MPI_Send/MPI_Recv pairs....
      */
-    int relion_MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm);
+    int relion_MPI_Send(void *buf, std::ptrdiff_t count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm);
 
-    int relion_MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status &status);
+    int relion_MPI_Recv(void *buf, std::ptrdiff_t count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status &status);
 
     int relion_MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm);
 
diff --git a/src/multidim_array.h b/src/multidim_array.h
index 7ab11a4..b1e2100 100644
--- a/src/multidim_array.h
+++ b/src/multidim_array.h
@@ -267,41 +267,6 @@ extern std::string floatToString(float F, int _width, int _prec);
         for (long int i=STARTINGY(V); i<=FINISHINGY(V); i++) \
             for (long int j=STARTINGX(V); j<=FINISHINGX(V); j++)
 
-/** For all elements in common.
- *
- * This macro is used to generate loops for all the elements logically in common
- * between two volumes in an easy manner. Then k, i and j (locally defined)
- * range from
- *
- * MAX(STARTINGZ(V1),STARTINGZ(V2)) to MIN(FINISHINGZ(V1),FINISHINGZ(V2)),
- * MAX(STARTINGY(V1),STARTINGY(V2)) to MIN(FINISHINGY(V1),FINISHINGY(V2)),
- * MAX(STARTINGX(V1),STARTINGX(V2)) to MIN(FINISHINGX(V1),FINISHINGX(V2))
- *
- * (included limits) respectively. You need to define SPEED_UP_temps.
- *
- * @code
- * SPEED_UP_temps;
- * MultidimArray< double > V1(10, 10, 10), V2(20, 20, 20);
- * V1.setXmippOrigin();
- * V2.setXmippOrigin();
- *
- * FOR_ALL_ELEMENTS_IN_COMMON_IN_ARRAY3D(V1, V2)
- * {
- *    // ...
- * }
- * @endcode
- */
-#define FOR_ALL_ELEMENTS_IN_COMMON_IN_ARRAY3D(V1, V2) \
-    ispduptmp0 = XMIPP_MAX(STARTINGZ(V1), STARTINGZ(V2)); \
-    ispduptmp1 = XMIPP_MIN(FINISHINGZ(V1),FINISHINGZ(V2)); \
-    ispduptmp2 = XMIPP_MAX(STARTINGY(V1), STARTINGY(V2)); \
-    ispduptmp3 = XMIPP_MIN(FINISHINGY(V1),FINISHINGY(V2)); \
-    ispduptmp4 = XMIPP_MAX(STARTINGX(V1), STARTINGX(V2)); \
-    ispduptmp5 = XMIPP_MIN(FINISHINGX(V1),FINISHINGX(V2)); \
-    for (long int k=ispduptmp0; k<=ispduptmp1; k++) \
-        for (long int i=ispduptmp2; i<=ispduptmp3; i++) \
-            for (long int j=ispduptmp4; j<=ispduptmp5; j++)
-
 /** For all direct elements in the array.
  *
  * This macro is used to generate loops for the volume in an easy way. It
@@ -587,7 +552,7 @@ public:
      * different memory assignment.
      *
      * @code
-     * MultidimArray< double > V2(V1);
+     * MultidimArray< DOUBLE > V2(V1);
      * @endcode
      */
     MultidimArray(const MultidimArray<T>& V)
@@ -1391,7 +1356,7 @@ public:
      * TRUE if the logical index given is outside the definition region of this
      * array.
      */
-    bool outside(const Matrix1D<double> &r) const
+    bool outside(const Matrix1D<DOUBLE> &r) const
     {
         if (r.size() < 1)
         {
@@ -1524,7 +1489,7 @@ public:
      * TRUE if the logical index given is a corner of the definition region of this
      * array.
      */
-    bool isCorner(const Matrix1D< double >& v) const
+    bool isCorner(const Matrix1D< DOUBLE >& v) const
     {
         if (v.size() < 2)
             REPORT_ERROR( "isCorner: index vector has got not enough components");
@@ -1551,7 +1516,7 @@ public:
     ///@name Access to the pixel values
     //@{
 
-    /** Volume element access via double vector.
+    /** Volume element access via DOUBLE vector.
      *
      * Returns the value of a matrix logical position, but this time the
      * element position is determined by a R3 vector. The elements can be used
@@ -1566,7 +1531,7 @@ public:
      * val = V(vectorR3(1, -2, 0));
      * @endcode
      */
-    T& operator()(const Matrix1D< double >& v) const
+    T& operator()(const Matrix1D< DOUBLE >& v) const
     {
         switch (VEC_XSIZE(v))
         {
@@ -1826,7 +1791,7 @@ public:
      * choosen column.
      *
      * @code
-     * std::vector< double > v;
+     * std::vector< DOUBLE > v;
      * m.getCol(-1, v);
      * @endcode
      */
@@ -1877,7 +1842,7 @@ public:
      * logical not physical.
      *
      * @code
-     * std::vector< double > v;
+     * std::vector< DOUBLE > v;
      * m.getRow(-2, v);
      * @endcode
      */
@@ -2010,18 +1975,18 @@ public:
      *
      * (x,y,z) are in logical coordinates.
      */
-    T interpolatedElement3D(double x, double y, double z, T outside_value = (T) 0, long int n = 0)
+    T interpolatedElement3D(DOUBLE x, DOUBLE y, DOUBLE z, T outside_value = (T) 0, long int n = 0)
     {
         long int x0 = FLOOR(x);
-        double fx = x - x0;
+        DOUBLE fx = x - x0;
         long int x1 = x0 + 1;
 
         long int y0 = FLOOR(y);
-        double fy = y - y0;
+        DOUBLE fy = y - y0;
         long int y1 = y0 + 1;
 
         long int z0 = FLOOR(z);
-        double fz = z - z0;
+        DOUBLE fz = z - z0;
         long int z1 = z0 + 1;
 
         T d000 = (outside(z0, y0, x0)) ? outside_value : NZYX_ELEM(*this, n, z0, y0, x0);
@@ -2033,12 +1998,12 @@ public:
         T d110 = (outside(z1, y1, x0)) ? outside_value : NZYX_ELEM(*this, n, z1, y1, x0);
         T d111 = (outside(z1, y1, x1)) ? outside_value : NZYX_ELEM(*this, n, z1, y1, x1);
 
-        double dx00 = LIN_INTERP(fx, (double) d000, (double) d001);
-        double dx01 = LIN_INTERP(fx, (double) d100, (double) d101);
-        double dx10 = LIN_INTERP(fx, (double) d010, (double) d011);
-        double dx11 = LIN_INTERP(fx, (double) d110, (double) d111);
-        double dxy0 = LIN_INTERP(fy, (double) dx00, (double) dx10);
-        double dxy1 = LIN_INTERP(fy, (double) dx01, (double) dx11);
+        DOUBLE dx00 = LIN_INTERP(fx, (DOUBLE) d000, (DOUBLE) d001);
+        DOUBLE dx01 = LIN_INTERP(fx, (DOUBLE) d100, (DOUBLE) d101);
+        DOUBLE dx10 = LIN_INTERP(fx, (DOUBLE) d010, (DOUBLE) d011);
+        DOUBLE dx11 = LIN_INTERP(fx, (DOUBLE) d110, (DOUBLE) d111);
+        DOUBLE dxy0 = LIN_INTERP(fy, (DOUBLE) dx00, (DOUBLE) dx10);
+        DOUBLE dxy1 = LIN_INTERP(fy, (DOUBLE) dx01, (DOUBLE) dx11);
 
         return (T) LIN_INTERP(fz, dxy0, dxy1);
     }
@@ -2047,13 +2012,13 @@ public:
      *
      * Bilinear interpolation. (x,y) are in logical coordinates.
      */
-    inline T interpolatedElement2D(double x, double y, T outside_value = (T) 0, long int n = 0) const
+    inline T interpolatedElement2D(DOUBLE x, DOUBLE y, T outside_value = (T) 0, long int n = 0) const
     {
         long int x0 = FLOOR(x);
-        double fx = x - x0;
+        DOUBLE fx = x - x0;
         long int x1 = x0 + 1;
         long int y0 = FLOOR(y);
-        double fy = y - y0;
+        DOUBLE fy = y - y0;
         long int y1 = y0 + 1;
 
         T d00 = outside(y0, x0) ? outside_value : NZYX_ELEM(*this, n, 0, y0, x0);
@@ -2061,8 +2026,8 @@ public:
         T d11 = outside(y1, x1) ? outside_value : NZYX_ELEM(*this, n, 0, y1, x1);
         T d01 = outside(y0, x1) ? outside_value : NZYX_ELEM(*this, n, 0, y0, x1);
 
-        double d0 = (T) LIN_INTERP(fx, (double) d00, (double) d01);
-        double d1 = (T) LIN_INTERP(fx, (double) d10, (double) d11);
+        DOUBLE d0 = (T) LIN_INTERP(fx, (DOUBLE) d00, (DOUBLE) d01);
+        DOUBLE d1 = (T) LIN_INTERP(fx, (DOUBLE) d10, (DOUBLE) d11);
         return (T) LIN_INTERP(fy, d0, d1);
     }
     //@}
@@ -2084,7 +2049,7 @@ public:
     void printStats(std::ostream& out = std::cout) const
     {
         T minval, maxval;
-        double avgval, devval;
+        DOUBLE avgval, devval;
 
         computeStats(avgval, devval, minval, maxval);
 
@@ -2271,14 +2236,14 @@ public:
 
     /** Minimum and maximum of the values in the array.
      *
-     * As doubles.
+     * As DOUBLEs.
      */
-    void computeDoubleMinMax(double& minval, double& maxval) const
+    void computeDoubleMinMax(DOUBLE& minval, DOUBLE& maxval) const
     {
         if (NZYXSIZE(*this) <= 0)
             return;
 
-        minval = maxval = static_cast< double >(data[0]);
+        minval = maxval = static_cast< DOUBLE >(data[0]);
 
         T* ptr=NULL;
         long int n;
@@ -2286,28 +2251,28 @@ public:
         {
             T val=*ptr;
             if (val < minval)
-                minval = static_cast< double >(val);
+                minval = static_cast< DOUBLE >(val);
             else if (val > maxval)
-                maxval = static_cast< double >(val);
+                maxval = static_cast< DOUBLE >(val);
         }
     }
 
     /** Average of the values in the array.
      *
-     * The returned value is always double, independently of the type of the
+     * The returned value is always DOUBLE, independently of the type of the
      * array.
      */
-    double computeAvg() const
+    DOUBLE computeAvg() const
     {
         if (NZYXSIZE(*this) <= 0)
             return 0;
 
-        double sum = 0;
+        DOUBLE sum = 0;
 
         T* ptr=NULL;
         long int n;
         FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY_ptr(*this,n,ptr)
-        sum += static_cast< double >(*ptr);
+        sum += static_cast< DOUBLE >(*ptr);
 
         return sum / NZYXSIZE(*this);
     }
@@ -2315,21 +2280,21 @@ public:
     /** Standard deviation of the values in the array.
      *
      * Be careful that the standard deviation and NOT the variance is returned.
-     * The returned value is always double, independently of the type of the
+     * The returned value is always DOUBLE, independently of the type of the
      * array.
      */
-    double computeStddev() const
+    DOUBLE computeStddev() const
     {
         if (NZYXSIZE(*this) <= 1)
             return 0;
 
-        double avg = 0, stddev = 0;
+        DOUBLE avg = 0, stddev = 0;
 
         T* ptr=NULL;
         long int n;
         FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY_ptr(*this,n,ptr)
         {
-            double val=static_cast< double >(*ptr);
+            DOUBLE val=static_cast< DOUBLE >(*ptr);
             avg += val;
             stddev += val * val;
         }
@@ -2339,7 +2304,7 @@ public:
         stddev *= NZYXSIZE(*this) / (NZYXSIZE(*this) - 1);
 
         // Foreseeing numerical instabilities
-        stddev = sqrt(static_cast<double>((ABS(stddev))));
+        stddev = sqrt(static_cast<DOUBLE>((ABS(stddev))));
 
         return stddev;
     }
@@ -2349,7 +2314,7 @@ public:
      * The average, standard deviation, minimum and maximum value are
      * returned.
      */
-    void computeStats(double& avg, double& stddev, T& minval, T& maxval) const
+    void computeStats(DOUBLE& avg, DOUBLE& stddev, T& minval, T& maxval) const
     {
         if (NZYXSIZE(*this) <= 0)
             return;
@@ -2364,7 +2329,7 @@ public:
         FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY_ptr(*this,n,ptr)
         {
             T Tval=*ptr;
-            double val=static_cast< double >(Tval);
+            DOUBLE val=static_cast< DOUBLE >(Tval);
             avg += val;
             stddev += val * val;
 
@@ -2382,7 +2347,7 @@ public:
             stddev *= NZYXSIZE(*this) / (NZYXSIZE(*this) - 1);
 
             // Foreseeing numerical instabilities
-            stddev = sqrt(static_cast< double >(ABS(stddev)));
+            stddev = sqrt(static_cast< DOUBLE >(ABS(stddev)));
         }
         else
             stddev = 0;
@@ -2396,7 +2361,7 @@ public:
      * med = v1.computeMedian();
      * @endcode
      */
-    double computeMedian() const
+    DOUBLE computeMedian() const
     {
         if (XSIZE(*this) == 0)
             return 0;
@@ -2405,7 +2370,7 @@ public:
             return DIRECT_MULTIDIM_ELEM(*this,0);
 
         // Initialise data
-        MultidimArray< double > temp(*this);
+        MultidimArray< DOUBLE > temp(*this);
 
         // Sort indexes
         temp.sort();
@@ -2434,15 +2399,15 @@ public:
         if (NZYXSIZE(*this) <= 0)
             return;
 
-        double min0, max0;
+        DOUBLE min0, max0;
         computeDoubleMinMax(min0, max0);
 
         // If max0==min0, it means that the vector is a constant one, so the
         // only possible transformation is to a fixed minF
-        double slope;
+        DOUBLE slope;
         if (max0 != min0)
-            slope = static_cast< double >(maxF - minF) /
-                    static_cast< double >(max0 - min0);
+            slope = static_cast< DOUBLE >(maxF - minF) /
+                    static_cast< DOUBLE >(max0 - min0);
         else
             slope = 0;
 
@@ -2450,7 +2415,7 @@ public:
         long int n;
         FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY_ptr(*this,n,ptr)
         *ptr = minF + static_cast< T >(slope *
-                                       static_cast< double >(*ptr - min0));
+                                       static_cast< DOUBLE >(*ptr - min0));
     }
 
     /** Adjust the range of the array to a given one within a mask.
@@ -2471,7 +2436,7 @@ public:
         if (MULTIDIM_SIZE(*this) <= 0)
             return;
 
-        double min0, max0;
+        DOUBLE min0, max0;
         bool first=true;
         T* ptr=NULL;
         long int n;
@@ -2483,7 +2448,7 @@ public:
                 T val= *ptr;
                 if (first)
                 {
-                    min0=max0=(double)val;
+                    min0=max0=(DOUBLE)val;
                     first=false;
                 }
                 else
@@ -2497,16 +2462,16 @@ public:
 
         // If max0==min0, it means that the vector is a constant one, so the
         // only possible transformation is to a fixed minF
-        double slope;
+        DOUBLE slope;
         if (max0 != min0)
-            slope = static_cast< double >(maxF - minF) /
-                    static_cast< double >(max0 - min0);
+            slope = static_cast< DOUBLE >(maxF - minF) /
+                    static_cast< DOUBLE >(max0 - min0);
         else
             slope = 0;
 
         FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY_ptr(*this,n,ptr)
         *ptr = minF + static_cast< T >(slope *
-                                       static_cast< double >(*ptr - min0));
+                                       static_cast< DOUBLE >(*ptr - min0));
     }
 
     /** Adjust the range of the array to the range of another array in
@@ -2517,9 +2482,9 @@ public:
      * (L2 sense) to the values of the array shown as sample
      */
 
-    //As written this will only work for T=double
+    //As written this will only work for T=DOUBLE
     //nevertheless since this is used is better
-    //to use T than double or will create problem for int multidim arrays
+    //to use T than DOUBLE or will create problem for int multidim arrays
     void rangeAdjust(const MultidimArray<T> &example,
                      const MultidimArray<int> *mask=NULL)
     {
@@ -2527,7 +2492,7 @@ public:
             return;
 
         // y=a+bx
-        double sumx=0, sumy=0, sumxy=0, sumx2=0;
+        DOUBLE sumx=0, sumy=0, sumxy=0, sumx2=0;
 
         T* ptrExample=MULTIDIM_ARRAY(example);
         int* ptrMask=NULL;
@@ -2535,7 +2500,7 @@ public:
             ptrMask=MULTIDIM_ARRAY(*mask);
         T* ptr=NULL;
         long int n;
-        double N=0;
+        DOUBLE N=0;
         FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY_ptr(*this,n,ptr)
         {
             bool process=true;
@@ -2556,10 +2521,10 @@ public:
             if (mask!=NULL)
                 ptrMask++;
         }
-        double b=(N*sumxy-sumx*sumy)/(N*sumx2-sumx*sumx);
-        double a=sumy/N-b*sumx/N;
+        DOUBLE b=(N*sumxy-sumx*sumy)/(N*sumx2-sumx*sumx);
+        DOUBLE a=sumy/N-b*sumx/N;
         FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY_ptr(*this,n,ptr)
-        *ptr = static_cast< double >(a+b * static_cast< double > (*ptr));
+        *ptr = static_cast< DOUBLE >(a+b * static_cast< DOUBLE > (*ptr));
     }
 
     /** Adjust the average and stddev of the array to given values.
@@ -2574,10 +2539,10 @@ public:
      * @endcode
      */
     // This function must be explictly implemented outside.
-    void statisticsAdjust(double avgF, double stddevF)
+    void statisticsAdjust(DOUBLE avgF, DOUBLE stddevF)
     {
-        double avg0, stddev0;
-        double a, b;
+        DOUBLE avg0, stddev0;
+        DOUBLE a, b;
 
         if (NZYXSIZE(*this) == 0)
             return;
@@ -2586,23 +2551,23 @@ public:
         computeStats(avg0, stddev0, minval, maxval);
 
         if (stddev0 != 0)
-            a = static_cast< double >(stddevF) / static_cast< double >(stddev0);
+            a = static_cast< DOUBLE >(stddevF) / static_cast< DOUBLE >(stddev0);
         else
             a = 0;
 
-        b = static_cast< double >(avgF) - a * static_cast< double >(avg0);
+        b = static_cast< DOUBLE >(avgF) - a * static_cast< DOUBLE >(avg0);
 
         T* ptr=NULL;
         long int n;
         FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY_ptr(*this,n,ptr)
-        *ptr = static_cast< T >(a * static_cast< double > (*ptr) + b);
+        *ptr = static_cast< T >(a * static_cast< DOUBLE > (*ptr) + b);
     }
     //@}
 
     /** @name Array "by" array operations.
      *
      * These are operations that are performed between 2 arrays of the
-     * SAME type (two integer vectors, two double matrices, ...). If they
+     * SAME type (two integer vectors, two DOUBLE matrices, ...). If they
      * are not of the same type you can convert one of the arrays to the
      * desired type using the function typeCast. The result must have been
      * defined to be of the same type as the operands.
@@ -2978,7 +2943,7 @@ public:
     /** Same value in all components.
      *
      * The constant must be of a type compatible with the array type, ie,
-     * you cannot  assign a double to an integer array without a casting.
+     * you cannot  assign a DOUBLE to an integer array without a casting.
      * It is not an error if the array is empty, then nothing is done.
      *
      * @code
@@ -3079,12 +3044,12 @@ public:
      */
     void initLinear(T minF, T maxF, int n = 1, const std::string& mode = "incr")
     {
-        double slope;
+        DOUBLE slope;
         int steps;
 
         if (mode == "incr")
         {
-            steps = 1 + (int) FLOOR((double) ABS((maxF - minF)) / ((double) n));
+            steps = 1 + (int) FLOOR((DOUBLE) ABS((maxF - minF)) / ((DOUBLE) n));
             slope = n * SGN(maxF - minF);
         }
         else if (mode == "steps")
@@ -3101,7 +3066,7 @@ public:
         {
             resize(steps);
             for (int i = 0; i < steps; i++)
-                A1D_ELEM(*this, i) = (T)((double) minF + slope * i);
+                A1D_ELEM(*this, i) = (T)((DOUBLE) minF + slope * i);
         }
     }
 
@@ -3126,7 +3091,7 @@ public:
      * // gaussian distribution with 0 m
ean and stddev=1
      * @endcode
      */
-    void initRandom(double op1, double op2, const std::string& mode = "uniform")
+    void initRandom(DOUBLE op1, DOUBLE op2, const std::string& mode = "uniform")
     {
         T* ptr=NULL;
         long int n;
@@ -3167,10 +3132,10 @@ public:
 
      * @endcode
      */
-    void addNoise(double op1,
-                  double op2,
+    void addNoise(DOUBLE op1,
+                  DOUBLE op2,
                   const std::string& mode = "uniform",
-                  double df = 3.) const
+                  DOUBLE df = 3.) const
     {
         T* ptr=NULL;
         unsigned long int n;
@@ -3200,7 +3165,7 @@ public:
      *
      * This function must be used only as a preparation for routines which need
      * that the first physical index is 1 and not 0 as it usually is in C. New
-     * memory is needed to hold the new double pointer array.
+     * memory is needed to hold the new DOUBLE pointer array.
      */
     T*** adaptForNumericalRecipes3D(long int n = 0) const
     {
@@ -3224,7 +3189,7 @@ public:
      *
      * This function must be used only as a preparation for routines which need
      * that the first physical index is 1 and not 0 as it usually is in C. New
-     * memory is needed to hold the new double pointer array.
+     * memory is needed to hold the new DOUBLE pointer array.
      */
     T** adaptForNumericalRecipes2D(long int n = 0) const
     {
@@ -3300,10 +3265,10 @@ public:
 
     /** Computes the center of mass of the nth array
      */
-    void centerOfMass(Matrix1D< double >& center, void * mask=NULL, long int n = 0)
+    void centerOfMass(Matrix1D< DOUBLE >& center, void * mask=NULL, long int n = 0)
     {
         center.initZeros(3);
-        double mass = 0;
+        DOUBLE mass = 0;
         MultidimArray< int >* imask = (MultidimArray< int >*) mask;
 
         FOR_ALL_ELEMENTS_IN_ARRAY3D(*this)
@@ -3531,7 +3496,7 @@ public:
      */
     void substitute(T oldv,
                     T newv,
-                    double accuracy = XMIPP_EQUAL_ACCURACY,
+                    DOUBLE accuracy = XMIPP_EQUAL_ACCURACY,
                     MultidimArray<int> * mask = NULL )
     {
         T* ptr=NULL;
@@ -3551,7 +3516,7 @@ public:
     void randomSubstitute(T oldv,
                           T avgv,
                           T sigv,
-                          double accuracy = XMIPP_EQUAL_ACCURACY,
+                          DOUBLE accuracy = XMIPP_EQUAL_ACCURACY,
                           MultidimArray<int> * mask = NULL )
     {
         T* ptr=NULL;
@@ -3568,8 +3533,8 @@ public:
      * than val+accuracy by 1 and the rest are set to 0. Use threshold to get a
      * very powerful binarization.
      */
-    void binarize(double val = 0,
-                  double accuracy = XMIPP_EQUAL_ACCURACY,
+    void binarize(DOUBLE val = 0,
+                  DOUBLE accuracy = XMIPP_EQUAL_ACCURACY,
                   MultidimArray<int> * mask = NULL )
     {
         T* ptr=NULL;
@@ -3686,7 +3651,7 @@ public:
         T* ptr=NULL;
         long int n;
         FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY_ptr(*this,n,ptr)
-        *ptr = static_cast< T >(sqrt(static_cast< double >(*ptr)));
+        *ptr = static_cast< T >(sqrt(static_cast< DOUBLE >(*ptr)));
     }
 
     /** Sum of matrix values.
@@ -3694,12 +3659,12 @@ public:
      * This function returns the sum of all internal values.
      *
      * @code
-     * double sum = m.sum();
+     * DOUBLE sum = m.sum();
      * @endcode
      */
-    double sum() const
+    DOUBLE sum() const
     {
-        double sum = 0;
+        DOUBLE sum = 0;
         T* ptr=NULL;
         long int n;
         FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY_ptr(*this,n,ptr)
@@ -3713,12 +3678,12 @@ public:
      * power_class.
      *
      * @code
-     * double sum2 = m.sum2();
+     * DOUBLE sum2 = m.sum2();
      * @endcode
      */
-    double sum2() const
+    DOUBLE sum2() const
     {
-        double sum = 0;
+        DOUBLE sum = 0;
         T* ptr=NULL;
         long int n;
         FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY_ptr(*this,n,ptr)
@@ -3735,7 +3700,7 @@ public:
         T* ptr=NULL;
         long int n;
         FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY_ptr(*this,n,ptr)
-        *ptr = static_cast< T >(log10(static_cast< double >(*ptr)));
+        *ptr = static_cast< T >(log10(static_cast< DOUBLE >(*ptr)));
     }
 
     /** Reverse matrix values over X axis, keep in this object.
@@ -3879,13 +3844,13 @@ public:
      * in the line is N.
      */
     void profile(long int x0, long int y0, long int xF, long int yF, long int N,
-                 MultidimArray< double >& profile) const
+                 MultidimArray< DOUBLE >& profile) const
     {
         checkDimension(2);
         profile.initZeros(N);
-        double tx_step = (double)(xF - x0) / (N - 1);
-        double ty_step = (double)(yF - y0) / (N - 1);
-        double tx = x0, ty = y0;
+        DOUBLE tx_step = (DOUBLE)(xF - x0) / (N - 1);
+        DOUBLE ty_step = (DOUBLE)(yF - y0) / (N - 1);
+        DOUBLE tx = x0, ty = y0;
 
         for (long int i = 0; i < N; i++)
         {
@@ -4099,7 +4064,7 @@ public:
      * than the argument and the same values (within accuracy).
      */
     bool equal(const MultidimArray<T>& op,
-               double accuracy = XMIPP_EQUAL_ACCURACY) const
+               DOUBLE accuracy = XMIPP_EQUAL_ACCURACY) const
     {
         if (!sameShape(op) || data==NULL || op.data == NULL)
             return false;
@@ -4117,7 +4082,7 @@ public:
 
 /** Conversion from one type to another.
  *
- * If we have an integer array and we need a double one, we can use this
+ * If we have an integer array and we need a DOUBLE one, we can use this
  * function. The conversion is done through a type casting of each element
  * If n >= 0, only the nth volumes will be converted, otherwise all NSIZE volumes
  */
@@ -4150,7 +4115,7 @@ void typeCast(const MultidimArray<T1>& v1,  MultidimArray<T2>& v2, long n = -1)
 /** Force positive.
  *  A median filter is applied at those negative values. Positive values are untouched.
  */
-void forcePositive(MultidimArray<double> &V);
+void forcePositive(MultidimArray<DOUBLE> &V);
 
 /** MultidimArray equality.*/
 template<typename T>
@@ -4173,12 +4138,12 @@ bool operator!=(const MultidimArray<T>& op1, const MultidimArray<T>& op2)
  * computed.
  *
  * @code
- * MultidimArray< double > V1(4, 5, 3);
+ * MultidimArray< DOUBLE > V1(4, 5, 3);
  * V1.startingX() = -2;
  * V1.startingY() = -2;
  * V1.startingZ() = -2;
  *
- * MultidimArray< double > V2(4, 2, 3);
+ * MultidimArray< DOUBLE > V2(4, 2, 3);
  * V2.startingX() = 0;
  * V2.startingY() = 0;
  * V2.startingZ() = 0;
@@ -4212,7 +4177,7 @@ std::ostream& operator<< (std::ostream& ostrm, const MultidimArray<T>& v)
     else
         ostrm << std::endl;
 
-    double max_val = ABS(DIRECT_A3D_ELEM(v , 0, 0, 0));
+    DOUBLE max_val = ABS(DIRECT_A3D_ELEM(v , 0, 0, 0));
 
     T* ptr;
     long int n;
@@ -4224,7 +4189,7 @@ std::ostream& operator<< (std::ostream& ostrm, const MultidimArray<T>& v)
     if (YSIZE(v)==1 && ZSIZE(v)==1)
     {
         for (long int j = STARTINGX(v); j <= FINISHINGX(v); j++)
-            ostrm << floatToString((double) A3D_ELEM(v, 0, 0, j), 10, prec)
+            ostrm << floatToString((DOUBLE) A3D_ELEM(v, 0, 0, j), 10, prec)
             << std::endl;
     }
     else
@@ -4241,7 +4206,7 @@ std::ostream& operator<< (std::ostream& ostrm, const MultidimArray<T>& v)
                 {
                     for (long int j = STARTINGX(v); j <= FINISHINGX(v); j++)
                     {
-                        ostrm << floatToString((double) A3D_ELEM(v, k, i, j), 10, prec) << ' ';
+                        ostrm << floatToString((DOUBLE) A3D_ELEM(v, k, i, j), 10, prec) << ' ';
                     }
                     ostrm << std::endl;
                 }
diff --git a/src/numerical_recipes.cpp b/src/numerical_recipes.cpp
index 495774f..4ff3183 100644
--- a/src/numerical_recipes.cpp
+++ b/src/numerical_recipes.cpp
@@ -58,133 +58,14 @@ void nrerror(const char error_text[])
 }
 #define NRSIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
 
-/* RANDOM NUMBERS ---------------------------------------------------------- */
-#define M1 259200
-#define IA1 7141
-#define IC1 54773
-#define RM1 (1.0/M1)
-#define M2 134456
-#define IA2 8121
-#define IC2 28411
-#define RM2 (1.0/M2)
-#define M3 243000
-#define IA3 4561
-#define IC3 51349
-
-/* Chapter 7 Section 1: UNIFORM RANDOM NUMBERS */
-double ran1(int *idum)
-{
-    static long ix1, ix2, ix3;
-    static double r[98];
-    double temp;
-    static int iff = 0;
-    int j;
-
-    if (*idum < 0 || iff == 0)
-    {
-        iff = 1;
-        ix1 = (IC1 - (*idum)) % M1;
-        ix1 = (IA1 * ix1 + IC1) % M1;
-        ix2 = ix1 % M2;
-        ix1 = (IA1 * ix1 + IC1) % M1;
-        ix3 = ix1 % M3;
-        for (j = 1;j <= 97;j++)
-        {
-            ix1 = (IA1 * ix1 + IC1) % M1;
-            ix2 = (IA2 * ix2 + IC2) % M2;
-            r[j] = (ix1 + ix2 * RM2) * RM1;
-        }
-        *idum = 1;
-    }
-    ix1 = (IA1 * ix1 + IC1) % M1;
-    ix2 = (IA2 * ix2 + IC2) % M2;
-    ix3 = (IA3 * ix3 + IC3) % M3;
-    j = 1 + ((97 * ix3) / M3);
-    if (j > 97 || j < 1)
-        nrerror("RAN1: This cannot happen.");
-    temp = r[j];
-    r[j] = (ix1 + ix2 * RM2) * RM1;
-    return temp;
-}
-
-#undef M1
-#undef IA1
-#undef IC1
-#undef RM1
-#undef M2
-#undef IA2
-#undef IC2
-#undef RM2
-#undef M3
-#undef IA3
-#undef IC3
-
-/* Chapter 7 Section 3: GAUSSIAN RANDOM NUMBERS */
-double gasdev(int *idum)
-{
-    static int iset = 0;
-    static double gset;
-    double fac, r, v1, v2;
-
-    if (iset == 0)
-    {
-        do
-        {
-            v1 = 2.0 * ran1(idum) - 1.0;
-            v2 = 2.0 * ran1(idum) - 1.0;
-            r = v1 * v1 + v2 * v2;
-        }
-        while (r >= 1.0);
-        fac = sqrt(-2.0 * log(r) / r);
-        gset = v1 * fac;
-        iset = 1;
-        return v2*fac;
-    }
-    else
-    {
-        iset = 0;
-        return gset;
-    }
-}
-
-// t-distribution (nor Numerical Recipes, but Mathematics of Computation, vol. 62, 779-781.
-// I downloaded sem-code from http://ideas.repec.org/c/ega/comcod/200703.html
-// Sjors May 2008
-double tdev(double nu, int *idum)
-{
-    static int iset = 0;
-    static double gset;
-    double fac, r, v1, v2;
-
-    if (iset == 0)
-    {
-        do
-        {
-            v1 = 2.0 * ran1(idum) - 1.0;
-            v2 = 2.0 * ran1(idum) - 1.0;
-            r = v1 * v1 + v2 * v2;
-        }
-        while (r >= 1.0);
-        fac = sqrt(nu*(pow(r,-2.0/nu) -1.0)/r);
-        gset = v1 * fac;
-        iset = 1;
-        return v2*fac;
-    }
-    else
-    {
-        iset = 0;
-        return gset;
-    }
-}
-
 
 /* BESSEL FUNCTIONS -------------------------------------------------------- */
 /* CO: They may not come in the numerical recipes but it is not a bad
    idea to put them here, in fact they come from Gabor's group in Feb'84     */
-double bessj0(double x)
+DOUBLE bessj0(DOUBLE x)
 {
-    double ax, z;
-    double xx, y, ans, ans1, ans2;
+    DOUBLE ax, z;
+    DOUBLE xx, y, ans, ans1, ans2;
 
     if ((ax = fabs(x)) < 8.0)
     {
@@ -217,9 +98,9 @@ double bessj0(double x)
 }
 
 /*............................................................................*/
-double bessi0(double x)
+DOUBLE bessi0(DOUBLE x)
 {
-    double y, ax, ans;
+    DOUBLE y, ax, ans;
     if ((ax = fabs(x)) < 3.75)
     {
         y = x / 3.75;
@@ -239,10 +120,10 @@ double bessi0(double x)
 }
 
 /*............................................................................*/
-double bessi1(double x)
+DOUBLE bessi1(DOUBLE x)
 {
-    double ax, ans;
-    double y;
+    DOUBLE ax, ans;
+    DOUBLE y;
     if ((ax = fabs(x)) < 3.75)
     {
         y = x / 3.75;
@@ -263,9 +144,9 @@ double bessi1(double x)
 }
 
 /* General Bessel functions ------------------------------------------------ */
-double chebev(double a, double b, double c[], int m, double x)
+DOUBLE chebev(DOUBLE a, DOUBLE b, DOUBLE c[], int m, DOUBLE x)
 {
-    double d = 0.0, dd = 0.0, sv, y, y2;
+    DOUBLE d = 0.0, dd = 0.0, sv, y, y2;
     int j;
 
     if ((x - a)*(x - b) > 0.0)
@@ -282,16 +163,16 @@ double chebev(double a, double b, double c[], int m, double x)
 #define NUSE1 5
 #define NUSE2 5
 
-void beschb(double x, double *gam1, double *gam2, double *gampl, double *gammi)
+void beschb(DOUBLE x, DOUBLE *gam1, DOUBLE *gam2, DOUBLE *gampl, DOUBLE *gammi)
 {
-    double xx;
-    static double c1[] =
+    DOUBLE xx;
+    static DOUBLE c1[] =
         {
             -1.142022680371172e0, 6.516511267076e-3,
             3.08709017308e-4, -3.470626964e-6, 6.943764e-9,
             3.6780e-11, -1.36e-13
         };
-    static double c2[] =
+    static DOUBLE c2[] =
         {
             1.843740587300906e0, -0.076852840844786e0,
             1.271927136655e-3, -4.971736704e-6, -3.3126120e-8,
@@ -312,10 +193,10 @@ void beschb(double x, double *gam1, double *gam2, double *gampl, double *gammi)
 #define FPMIN 1.0e-30
 #define MAXIT 10000
 #define XMIN 2.0
-void bessjy(double x, double xnu, double *rj, double *ry, double *rjp, double *ryp)
+void bessjy(DOUBLE x, DOUBLE xnu, DOUBLE *rj, DOUBLE *ry, DOUBLE *rjp, DOUBLE *ryp)
 {
     int i, isign, l, nl;
-    double a, b, br, bi, c, cr, ci, d, del, del1, den, di, dlr, dli, dr, e, f, fact, fact2,
+    DOUBLE a, b, br, bi, c, cr, ci, d, del, del1, den, di, dlr, dli, dr, e, f, fact, fact2,
     fact3, ff, gam, gam1, gam2, gammi, gampl, h, p, pimu, pimu2, q, r, rjl,
     rjl1, rjmu, rjp1, rjpl, rjtemp, ry1, rymu, rymup, rytemp, sum, sum1,
     temp, w, x2, xi, xi2, xmu, xmu2;
@@ -478,52 +359,52 @@ void bessjy(double x, double xnu, double *rj, double *ry, double *rjp, double *r
 #undef XMIN
 
 /*............................................................................*/
-double bessi0_5(double x)
+DOUBLE bessi0_5(DOUBLE x)
 {
     return (x == 0) ? 0 : sqrt(2 / (PI*x))*sinh(x);
 }
-double bessi1_5(double x)
+DOUBLE bessi1_5(DOUBLE x)
 {
     return (x == 0) ? 0 : sqrt(2 / (PI*x))*(cosh(x) - sinh(x) / x);
 }
-double bessi2(double x)
+DOUBLE bessi2(DOUBLE x)
 {
     return (x == 0) ? 0 : bessi0(x) - ((2*1) / x) * bessi1(x);
 }
-double bessi2_5(double x)
+DOUBLE bessi2_5(DOUBLE x)
 {
     return (x == 0) ? 0 : bessi0_5(x) - ((2*1.5) / x) * bessi1_5(x);
 }
-double bessi3(double x)
+DOUBLE bessi3(DOUBLE x)
 {
     return (x == 0) ? 0 : bessi1(x) - ((2*2) / x) * bessi2(x);
 }
-double bessi3_5(double x)
+DOUBLE bessi3_5(DOUBLE x)
 {
     return (x == 0) ? 0 : bessi1_5(x) - ((2*2.5) / x) * bessi2_5(x);
 }
-double bessi4(double x)
+DOUBLE bessi4(DOUBLE x)
 {
     return (x == 0) ? 0 : bessi2(x) - ((2*3) / x) * bessi3(x);
 }
-double bessj1_5(double x)
+DOUBLE bessj1_5(DOUBLE x)
 {
-    double rj, ry, rjp, ryp;
+    DOUBLE rj, ry, rjp, ryp;
     bessjy(x, 1.5, &rj, &ry, &rjp, &ryp);
     return rj;
 }
-double bessj3_5(double x)
+DOUBLE bessj3_5(DOUBLE x)
 {
-    double rj, ry, rjp, ryp;
+    DOUBLE rj, ry, rjp, ryp;
     bessjy(x, 3.5, &rj, &ry, &rjp, &ryp);
     return rj;
 }
 
 /* Special functions ------------------------------------------------------- */
-double gammln(double xx)
+DOUBLE gammln(DOUBLE xx)
 {
-    double x, tmp, ser;
-    static double cof[6] =
+    DOUBLE x, tmp, ser;
+    static DOUBLE cof[6] =
         {
             76.18009173, -86.50532033, 24.01409822,
             -1.231739516, 0.120858003e-2, -0.536382e-5
@@ -543,9 +424,9 @@ double gammln(double xx)
 }
 
 
-double betai(double a, double b, double x)
+DOUBLE betai(DOUBLE a, DOUBLE b, DOUBLE x)
 {
-    double bt;
+    DOUBLE bt;
     if (x < 0.0 || x > 1.0)
         nrerror("Bad x in routine BETAI");
     if (x == 0.0 || x == 1.0)
@@ -561,11 +442,11 @@ double betai(double a, double b, double x)
 
 #define ITMAX 100
 #define EPS 3.0e-7
-double betacf(double a, double b, double x)
+DOUBLE betacf(DOUBLE a, DOUBLE b, DOUBLE x)
 {
-    double qap, qam, qab, em, tem, d;
-    double bz, bm = 1.0, bp, bpp;
-    double az = 1.0, am = 1.0, ap, app, aold;
+    DOUBLE qap, qam, qab, em, tem, d;
+    DOUBLE bz, bm = 1.0, bp, bpp;
+    DOUBLE az = 1.0, am = 1.0, ap, app, aold;
     int m;
 
     qab = a + b;
@@ -574,7 +455,7 @@ double betacf(double a, double b, double x)
     bz = 1.0 - qab * x / qap;
     for (m = 1;m <= ITMAX;m++)
     {
-        em = (double) m;
+        em = (DOUBLE) m;
         tem = em + em;
         d = em * (b - em) * x / ((qam + tem) * (a + tem));
         ap = az + d * am;
@@ -610,12 +491,12 @@ double betacf(double a, double b, double x)
         xt[j] = pcom[j] + x * xicom[j]; \
     f = (*func)(xt,prm);}
 
-void mnbrak(double *ax, double *bx, double *cx,
-            double *fa, double *fb, double *fc, double(*func)(double *, void*),
-            void *prm, int ncom, double *pcom, double *xicom)
+void mnbrak(DOUBLE *ax, DOUBLE *bx, DOUBLE *cx,
+            DOUBLE *fa, DOUBLE *fb, DOUBLE *fc, DOUBLE(*func)(DOUBLE *, void*),
+            void *prm, int ncom, DOUBLE *pcom, DOUBLE *xicom)
 {
-    double ulim, u, r, q, fu, dum;
-    double *xt=NULL;
+    DOUBLE ulim, u, r, q, fu, dum;
+    DOUBLE *xt=NULL;
     ask_Tvector(xt, 1, ncom);
 
     F1DIM(*ax,*fa);
@@ -660,7 +541,7 @@ void mnbrak(double *ax, double *bx, double *cx,
             if (fu < *fc)
             {
                 SHFT(*bx, *cx, u, *cx + GOLD*(*cx - *bx))
-                double aux; F1DIM(u,aux);
+                DOUBLE aux; F1DIM(u,aux);
                 SHFT(*fb, *fc, fu, aux)
             }
         }
@@ -688,14 +569,14 @@ void mnbrak(double *ax, double *bx, double *cx,
 #define ITMAX 100
 #define CGOLD 0.3819660
 #define ZEPS 1.0e-10
-double brent(double ax, double bx, double cx, double(*func)(double *,void*),
-             void *prm, double tol, double *xmin,
-             int ncom, double *pcom, double *xicom)
+DOUBLE brent(DOUBLE ax, DOUBLE bx, DOUBLE cx, DOUBLE(*func)(DOUBLE *,void*),
+             void *prm, DOUBLE tol, DOUBLE *xmin,
+             int ncom, DOUBLE *pcom, DOUBLE *xicom)
 {
     int iter;
-    double a, b, d, etemp, fu, fv, fw, fx, p, q, r, tol1, tol2, u, v, w, x, xm;
-    double e = 0.0;
-    double *xt=NULL;
+    DOUBLE a, b, d, etemp, fu, fv, fw, fx, p, q, r, tol1, tol2, u, v, w, x, xm;
+    DOUBLE e = 0.0;
+    DOUBLE *xt=NULL;
     ask_Tvector(xt, 1, ncom);
 
     a = (ax < cx ? ax : cx);
@@ -781,15 +662,15 @@ double brent(double ax, double bx, double cx, double(*func)(double *,void*),
 #undef F1DIM
 
 #define TOL 2.0e-4
-void linmin(double *p, double *xi, int n, double &fret,
-            double(*func)(double *, void*), void *prm)
+void linmin(DOUBLE *p, DOUBLE *xi, int n, DOUBLE &fret,
+            DOUBLE(*func)(DOUBLE *, void*), void *prm)
 {
     int j;
-    double xx, xmin, fx, fb, fa, bx, ax;
+    DOUBLE xx, xmin, fx, fb, fa, bx, ax;
 
     int ncom = n;
-    double *pcom=NULL;
-    double *xicom=NULL;
+    DOUBLE *pcom=NULL;
+    DOUBLE *xicom=NULL;
     ask_Tvector(pcom, 1, n);
     ask_Tvector(xicom, 1, n);
     for (j = 1;j <= n;j++)
@@ -813,13 +694,13 @@ void linmin(double *p, double *xi, int n, double &fret,
 #undef TOL
 
 #define ITMAX 200
-void powell(double *p, double *xi, int n, double ftol, int &iter,
-            double &fret, double(*func)(double *, void *), void *prm,
+void powell(DOUBLE *p, DOUBLE *xi, int n, DOUBLE ftol, int &iter,
+            DOUBLE &fret, DOUBLE(*func)(DOUBLE *, void *), void *prm,
             bool show)
 {
     int i, ibig, j;
-    double t, fptt, fp, del;
-    double *pt, *ptt, *xit;
+    DOUBLE t, fptt, fp, del;
+    DOUBLE *pt, *ptt, *xit;
     bool   different_from_0;
 
     ask_Tvector(pt, 1, n);
@@ -915,9 +796,9 @@ void powell(double *p, double *xi, int n, double ftol, int &iter,
 
 /* Singular value descomposition ------------------------------------------- */
 /* Copied from Bilib library (linearalgebra.h) */
-double Pythag(double a, double b)
+DOUBLE Pythag(DOUBLE a, DOUBLE b)
 {
-    double absa, absb;
+    DOUBLE absa, absb;
     absa = fabs(a);
     absb = fabs(b);
     if (absb < absa)
@@ -927,12 +808,12 @@ double Pythag(double a, double b)
 }
 
 #define SVDMAXITER 1000
-void svdcmp(double *U, int Lines, int Columns, double *W, double *V)
+void svdcmp(DOUBLE *U, int Lines, int Columns, DOUBLE *W, DOUBLE *V)
 {
-    double *rv1 = (double *)NULL;
-    double Norm, Scale;
-    double c, f, g, h, s;
-    double x, y, z;
+    DOUBLE *rv1 = (DOUBLE *)NULL;
+    DOUBLE Norm, Scale;
+    DOUBLE c, f, g, h, s;
+    DOUBLE x, y, z;
     long i, its, j, jj, k, l = 0L, nm = 0L;
     bool    Flag;
     int     MaxIterations = SVDMAXITER;
@@ -1216,10 +1097,10 @@ void svdcmp(double *U, int Lines, int Columns, double *W, double *V)
     free_Tvector(rv1, 0, Columns*Columns - 1);
 }
 
-void svbksb(double *u, double *w, double *v, int m, int n, double *b, double *x)
+void svbksb(DOUBLE *u, DOUBLE *w, DOUBLE *v, int m, int n, DOUBLE *b, DOUBLE *x)
 {
     int jj, j, i;
-    double s, *tmp;
+    DOUBLE s, *tmp;
 
     ask_Tvector(tmp, 1, n);
     for (j = 1;j <= n;j++)
@@ -1248,10 +1129,10 @@ void svbksb(double *u, double *w, double *v, int m, int n, double *b, double *x)
 #define ITMAX 100
 #define EPS 3.0e-7
 
-void gser(double *gamser, double a, double x, double *gln)
+void gser(DOUBLE *gamser, DOUBLE a, DOUBLE x, DOUBLE *gln)
 {
     int n;
-    double sum, del, ap;
+    DOUBLE sum, del, ap;
 
     *gln = gammln(a);
     if (x <= 0.0)
@@ -1287,10 +1168,10 @@ void gser(double *gamser, double a, double x, double *gln)
 #define EPS 3.0e-7
 #define FPMIN 1.0e-30
 
-void gcf(double *gammcf, double a, double x, double *gln)
+void gcf(DOUBLE *gammcf, DOUBLE a, DOUBLE x, DOUBLE *gln)
 {
     int i;
-    double an, b, c, d, del, h;
+    DOUBLE an, b, c, d, del, h;
 
     *gln = gammln(a);
     b = x + 1.0 - a;
@@ -1321,9 +1202,9 @@ void gcf(double *gammcf, double a, double x, double *gln)
 #undef EPS
 #undef FPMIN
 
-double gammp(double a, double x)
+DOUBLE gammp(DOUBLE a, DOUBLE x)
 {
-    double gamser, gammcf, gln;
+    DOUBLE gamser, gammcf, gln;
 
     if (x < 0.0 || a <= 0.0)
         nrerror("Invalid arguments in routine gammp");
diff --git a/src/numerical_recipes.h b/src/numerical_recipes.h
index 21b3f40..1c53660 100644
--- a/src/numerical_recipes.h
+++ b/src/numerical_recipes.h
@@ -60,39 +60,34 @@
 // Utilities --------------------------------------------------------------
 void nrerror(const char error_text[]);
 
-// Random numbers ---------------------------------------------------------
-double ran1(int *idum);                                 // Uniform random
-double gasdev(int *idum);                               // Gaussian random
-double tdev(double nu, int *idum);                      // t-student random
-
 // Bessel functions --------------------------------------------------------
-double bessj0(double x);
-double bessj3_5(double x);
-double bessj1_5(double x);
+DOUBLE bessj0(DOUBLE x);
+DOUBLE bessj3_5(DOUBLE x);
+DOUBLE bessj1_5(DOUBLE x);
 
-double bessi0(double x);
-double bessi1(double x);
-double bessi0_5(double x);
-double bessi1_5(double x);
-double bessi2(double x);
-double bessi3(double x);
-double bessi2_5(double x);
-double bessi3_5(double x);
-double bessi4(double x);
+DOUBLE bessi0(DOUBLE x);
+DOUBLE bessi1(DOUBLE x);
+DOUBLE bessi0_5(DOUBLE x);
+DOUBLE bessi1_5(DOUBLE x);
+DOUBLE bessi2(DOUBLE x);
+DOUBLE bessi3(DOUBLE x);
+DOUBLE bessi2_5(DOUBLE x);
+DOUBLE bessi3_5(DOUBLE x);
+DOUBLE bessi4(DOUBLE x);
 
 // Special functions -------------------------------------------------------
-double gammln(double xx);
-double gammp(double a, double x);
-double betacf(double a, double b, double x);
-double betai(double a, double b, double x);
+DOUBLE gammln(DOUBLE xx);
+DOUBLE gammp(DOUBLE a, DOUBLE x);
+DOUBLE betacf(DOUBLE a, DOUBLE b, DOUBLE x);
+DOUBLE betai(DOUBLE a, DOUBLE b, DOUBLE x);
 
 // Singular value descomposition of matrix a (numerical recipes, chapter 2-6 for details)
-void svdcmp(double *a, int m, int n, double *w, double *v);
-void svbksb(double *u, double *w, double *v, int m, int n, double *b, double *x);
+void svdcmp(DOUBLE *a, int m, int n, DOUBLE *w, DOUBLE *v);
+void svbksb(DOUBLE *u, DOUBLE *w, DOUBLE *v, int m, int n, DOUBLE *b, DOUBLE *x);
 
 // Optimization ------------------------------------------------------------
-void powell(double *p, double *xi, int n, double ftol, int &iter,
-            double &fret, double(*func)(double *, void *), void *prm,
+void powell(DOUBLE *p, DOUBLE *xi, int n, DOUBLE ftol, int &iter,
+            DOUBLE &fret, DOUBLE(*func)(DOUBLE *, void *), void *prm,
             bool show);
 
 // Working with matrices ---------------------------------------------------
@@ -112,7 +107,7 @@ void ludcmp(T *a, int n, int *indx, T *d)
     {
         big = (T)0.0;
         for (j = 1;j <= n;j++)
-            if ((temp = (T)fabs((double)a[i*n+j])) > big)
+            if ((temp = (T)fabs((DOUBLE)a[i*n+j])) > big)
                 big = temp;
         if (big == (T)0.0)
             nrerror("Singular matrix in routine LUDCMP");
@@ -134,7 +129,7 @@ void ludcmp(T *a, int n, int *indx, T *d)
             for (k = 1;k < j;k++)
                 sum -= a[i*n+k] * a[k*n+j];
             a[i*n+j] = sum;
-            if ((dum = vv[i] * (T)fabs((double)sum)) >= big)
+            if ((dum = vv[i] * (T)fabs((DOUBLE)sum)) >= big)
             {
                 big = dum;
                 imax = i;
@@ -203,7 +198,7 @@ void gaussj(T *a, int n, T *b, int m)
     int *indxc, *indxr, *ipiv;
     int i, icol, irow, j, k, l, ll;
     T big, dum;
-    double pivinv;
+    DOUBLE pivinv;
 
     ask_Tvector(indxc, 1, n);
     ask_Tvector(indxr, 1, n);
@@ -219,7 +214,7 @@ void gaussj(T *a, int n, T *b, int m)
                 {
                     if (ipiv[k] == 0)
                     {
-                        if (fabs((double)a[j*n+k]) >= (double) big)
+                        if (fabs((DOUBLE)a[j*n+k]) >= (DOUBLE) big)
                         {
                             big = ABS(a[j*n+k]);
                             irow = j;
diff --git a/src/parallel.cpp b/src/parallel.cpp
index 62ce9a9..2f98489 100644
--- a/src/parallel.cpp
+++ b/src/parallel.cpp
@@ -237,6 +237,7 @@ void ParallelTaskDistributor::resize(size_t nTasks, size_t bSize)
 	numberOfTasks = nTasks;
 	blockSize = bSize;
 	assignedTasks = 0;
+
 }
 
 void ParallelTaskDistributor::reset()
diff --git a/src/particle_polisher.cpp b/src/particle_polisher.cpp
index 0a97a71..f76192b 100644
--- a/src/particle_polisher.cpp
+++ b/src/particle_polisher.cpp
@@ -62,6 +62,7 @@ void ParticlePolisher::read(int argc, char **argv)
 	int norm_section = parser.addSection("Normalisation options");
 	do_normalise = !parser.checkOption("--skip_normalise", "Do not normalise the polsihed particles?");
 	bg_radius =  textToInteger(parser.getOption("--bg_radius", "Radius of the circular mask that will be used to define the background area (in pixels)", "-1"));
+	do_ramp = !parser.checkOption("--no_ramp", "Just subtract the background mean in the normalisation, instead of subtracting a fitted ramping background. ");
 	white_dust_stddev = textToFloat(parser.getOption("--white_dust", "Sigma-values above which white dust will be removed (negative value means no dust removal)","-1"));
 	black_dust_stddev = textToFloat(parser.getOption("--black_dust", "Sigma-values above which black dust will be removed (negative value means no dust removal)","-1"));
 
@@ -73,6 +74,7 @@ void ParticlePolisher::read(int argc, char **argv)
 	int out_section = parser.addSection("Polished particles output options");
 	first_frame = textToInteger(parser.getOption("--first_frame", "First frame to include in the polished particles", "1"));
 	last_frame = textToInteger(parser.getOption("--last_frame", "First frame to include in the polished particles (default is all)", "-1"));
+	step_frame  = textToInteger(parser.getOption("--avg_movie_frames", "Give the same value as used in particle extraction (usually just 1)", "1"));
 
 	verb = textToInteger(parser.getOption("--verb", "Verbosity", "1"));
 
@@ -96,15 +98,10 @@ void ParticlePolisher::initialise()
 #ifndef DEBUG_TILT
     if (verb > 0)
     	std::cout << " + Reading the input STAR file ... " << std::endl;
-    exp_model.read(fn_in, true); // true means do_ignore_particle_name!!
-
-	// Find out which OriginalParticles come from the same micrograph
-    if (verb > 0)
-    	std::cout << " + Grouping all particles from the same micrographs ... " << std::endl;
-	exp_model.getAverageMicrographs();
+    exp_model.read(fn_in, false, true); // false means NOT do_ignore_particle_name here, true means DO_ignore_group_name...
 
 	// Pre-size the fitted_movements array (for parallelised output...)
-	int total_nr_images = exp_model.numberOfParticles();
+	long int total_nr_images = exp_model.numberOfParticles();
 	fitted_movements.resize(total_nr_images, 2);
 
 	last_frame = (last_frame < 0) ? exp_model.ori_particles[0].particles_id.size() : last_frame;
@@ -114,7 +111,7 @@ void ParticlePolisher::initialise()
 	// Get the pixel size from the input STAR file
 	if (exp_model.MDimg.containsLabel(EMDL_CTF_MAGNIFICATION) && exp_model.MDimg.containsLabel(EMDL_CTF_DETECTOR_PIXEL_SIZE))
 	{
-		double mag, dstep;
+		DOUBLE mag, dstep;
 		exp_model.MDimg.getValue(EMDL_CTF_MAGNIFICATION, mag);
 		exp_model.MDimg.getValue(EMDL_CTF_DETECTOR_PIXEL_SIZE, dstep);
 		angpix = 10000. * dstep / mag;
@@ -124,6 +121,9 @@ void ParticlePolisher::initialise()
 
     if (do_weighting)
     {
+        if (fn_mask == "")
+            REPORT_ERROR("When doing B-factor weighting, you have to provide a mask!");
+
     	// Read in the Imask
     	Imask.read(fn_mask);
     	ori_size = XSIZE(Imask());
@@ -158,14 +158,14 @@ void ParticlePolisher::fitMovementsAllMicrographs()
 	}
 
     // Set the fitted movements in the xoff and yoff columns of the exp_model.MDimg
-    for (int ipart = 0; ipart < exp_model.numberOfParticles(); ipart++)
+    for (long int ipart = 0; ipart < exp_model.numberOfParticles(); ipart++)
+
 	{
 		long int part_id = exp_model.particles[ipart].id;
-		long int img_id = exp_model.getImageId(part_id, 0);
-		double xoff = DIRECT_A2D_ELEM(fitted_movements, img_id, 0);
-		double yoff = DIRECT_A2D_ELEM(fitted_movements, img_id, 1);
-		exp_model.MDimg.setValue(EMDL_ORIENT_ORIGIN_X, xoff, img_id);
-		exp_model.MDimg.setValue(EMDL_ORIENT_ORIGIN_Y, yoff, img_id);
+		DOUBLE xoff = DIRECT_A2D_ELEM(fitted_movements, part_id, 0);
+		DOUBLE yoff = DIRECT_A2D_ELEM(fitted_movements, part_id, 1);
+		exp_model.MDimg.setValue(EMDL_ORIENT_ORIGIN_X, xoff, part_id);
+		exp_model.MDimg.setValue(EMDL_ORIENT_ORIGIN_Y, yoff, part_id);
 	}
 
     if (verb > 0)
@@ -186,9 +186,9 @@ void ParticlePolisher::fitMovementsAllMicrographs()
 void ParticlePolisher::fitMovementsOneMicrograph(long int imic)
 {
 
-	std::vector<double> x_pick, y_pick, x_off_prior, y_off_prior, x_start, x_end, dummy; // X and Y-coordinates for the average particles in the micrograph
-	std::vector< std::vector<double> > x_off, y_off; // X and Y shifts w.r.t. the average for each movie frame
-	double x_pick_p, y_pick_p, x_off_p, y_off_p, x_off_prior_p, y_off_prior_p;
+	std::vector<DOUBLE> x_pick, y_pick, x_off_prior, y_off_prior, x_start, x_end, dummy; // X and Y-coordinates for the average particles in the micrograph
+	std::vector< std::vector<DOUBLE> > x_off, y_off; // X and Y shifts w.r.t. the average for each movie frame
+	DOUBLE x_pick_p, y_pick_p, x_off_p, y_off_p, x_off_prior_p, y_off_prior_p;
 
 
 	// Loop over all original_particles in this average_micrograph
@@ -203,19 +203,18 @@ void ParticlePolisher::fitMovementsOneMicrograph(long int imic)
 		for (long int i_frame = 0; i_frame < exp_model.ori_particles[ori_part_id].particles_id.size(); i_frame++ )
 		{
 			long int part_id = exp_model.ori_particles[ori_part_id].particles_id[i_frame];
-			long int img_id = exp_model.getImageId(part_id, 0);
 
-			exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_X, x_off_p, img_id);
-			exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_Y, y_off_p, img_id);
-			exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_X_PRIOR, x_off_prior_p, img_id);
-			exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_Y_PRIOR, y_off_prior_p, img_id);
+			exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_X, x_off_p, part_id);
+			exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_Y, y_off_p, part_id);
+			exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_X_PRIOR, x_off_prior_p, part_id);
+			exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_Y_PRIOR, y_off_prior_p, part_id);
 
 			// Store the value of the picked coordinates for the first movie frame
 			if (is_first)
 			{
 				is_first = false;
-				exp_model.MDimg.getValue(EMDL_IMAGE_COORD_X, x_pick_p, img_id);
-				exp_model.MDimg.getValue(EMDL_IMAGE_COORD_X, y_pick_p, img_id);
+				exp_model.MDimg.getValue(EMDL_IMAGE_COORD_X, x_pick_p, part_id);
+				exp_model.MDimg.getValue(EMDL_IMAGE_COORD_Y, y_pick_p, part_id);
 				x_pick.push_back(x_pick_p);
 				y_pick.push_back(y_pick_p);
 				x_off_prior.push_back(x_off_prior_p);
@@ -234,15 +233,15 @@ void ParticlePolisher::fitMovementsOneMicrograph(long int imic)
 	}
 
 	// Now do the actual fitting
-	double gauss_const = 1. / sqrt(2 * PI * sigma_neighbour_distance * sigma_neighbour_distance);
-	double min2sigma2 = - 2. * sigma_neighbour_distance * sigma_neighbour_distance;
+	DOUBLE gauss_const = 1. / sqrt(2 * PI * sigma_neighbour_distance * sigma_neighbour_distance);
+	DOUBLE min2sigma2 = - 2. * sigma_neighbour_distance * sigma_neighbour_distance;
 	// Loop over all ori_particles
 	for (long int ipar = 0; ipar < (exp_model.average_micrographs[imic]).ori_particles_id.size(); ipar++)
 	{
 		long int ori_part_id = exp_model.average_micrographs[imic].ori_particles_id[ipar];
 
-		double my_pick_x = x_pick[ipar] + x_off_prior[ipar];
-		double my_pick_y = y_pick[ipar] + y_off_prior[ipar];
+		DOUBLE my_pick_x = x_pick[ipar] + x_off_prior[ipar];
+		DOUBLE my_pick_y = y_pick[ipar] + y_off_prior[ipar];
 
 		fit_point2D      onepoint;
 		std::vector<fit_point2D> points_x, points_y;
@@ -251,10 +250,10 @@ void ParticlePolisher::fitMovementsOneMicrograph(long int imic)
 		for (long int ii = 0; ii < x_pick.size(); ii++)
 		{
 
-			double nb_pick_x = x_pick[ii] + x_off_prior[ii]; // add prior to center at average position
-			double nb_pick_y = y_pick[ii] + y_off_prior[ii]; // add prior to center at average position
-			double dist2 = (nb_pick_x - my_pick_x) * (nb_pick_x - my_pick_x) + (nb_pick_y - my_pick_y) * (nb_pick_y - my_pick_y);
-			double weight;
+			DOUBLE nb_pick_x = x_pick[ii] + x_off_prior[ii]; // add prior to center at average position
+			DOUBLE nb_pick_y = y_pick[ii] + y_off_prior[ii]; // add prior to center at average position
+			DOUBLE dist2 = (nb_pick_x - my_pick_x) * (nb_pick_x - my_pick_x) + (nb_pick_y - my_pick_y) * (nb_pick_y - my_pick_y);
+			DOUBLE weight;
 			if (ABS(min2sigma2) < 0.01)
 				weight = (dist2 < 0.01) ? 1. : 0.;
 			else
@@ -282,8 +281,8 @@ void ParticlePolisher::fitMovementsOneMicrograph(long int imic)
 			}
 		}
 
-		double slope_x, intercept_x, ccf_x;
-		double slope_y, intercept_y, ccf_y;
+		DOUBLE slope_x, intercept_x, ccf_x;
+		DOUBLE slope_y, intercept_y, ccf_y;
 		fitStraightLine(points_x, slope_x, intercept_x, ccf_x);
 		fitStraightLine(points_y, slope_y, intercept_y, ccf_y);
 
@@ -310,9 +309,8 @@ void ParticlePolisher::fitMovementsOneMicrograph(long int imic)
 			}
 
 			long int part_id = exp_model.ori_particles[ori_part_id].particles_id[i_frame];
-			long int img_id = exp_model.getImageId(part_id, 0);
-			A2D_ELEM(fitted_movements, img_id, 0) = x_off_p + x_off_prior[ipar];
-			A2D_ELEM(fitted_movements, img_id, 1) = y_off_p + y_off_prior[ipar];
+			A2D_ELEM(fitted_movements, part_id, 0) = x_off_p + x_off_prior[ipar];
+			A2D_ELEM(fitted_movements, part_id, 1) = y_off_p + y_off_prior[ipar];
 		}
 	}
 
@@ -329,7 +327,7 @@ void ParticlePolisher::calculateAllSingleFrameReconstructionsAndBfactors()
 		return;
 	}
 
-	double bfactor, offset, corr_coeff;
+	DOUBLE bfactor, offset, corr_coeff;
 
 	// Loop over all frames to be included in the reconstruction
 	int my_nr_frames = last_frame - first_frame + 1;
@@ -441,16 +439,16 @@ void ParticlePolisher::writeStarFileRelativeWeights(FileName fn_star)
         REPORT_ERROR( (std::string)"ParticlePolisher::writeStarFileRelativeWeights: Cannot write file: " + fn_star);
 
     // First pre-calculate the sum of all weights at every frequency
-    MultidimArray<double> sumweight_per_shell(ori_size/2), cumulative_relweight_per_shell(ori_size/2);
+    MultidimArray<DOUBLE> sumweight_per_shell(ori_size/2), cumulative_relweight_per_shell(ori_size/2);
     sumweight_per_shell.initZeros();
     for (int iframe = 0; iframe < XSIZE(perframe_bfactors)/3; iframe++ )
    	{
-    	double bfactor = DIRECT_A1D_ELEM(perframe_bfactors, iframe * 3 + 0);
-    	double offset = DIRECT_A1D_ELEM(perframe_bfactors, iframe * 3 + 1);
+    	DOUBLE bfactor = DIRECT_A1D_ELEM(perframe_bfactors, iframe * 3 + 0);
+    	DOUBLE offset = DIRECT_A1D_ELEM(perframe_bfactors, iframe * 3 + 1);
 		for (int i = 1; i < ori_size/2; i++) // ignore origin
 		{
-			double res = (double)i / (ori_size * angpix); // resolution in 1/A
-			double w = exp( (bfactor / 4.)  * res * res  + offset);
+			DOUBLE res = (DOUBLE)i / (ori_size * angpix); // resolution in 1/A
+			DOUBLE w = exp( (bfactor / 4.)  * res * res  + offset);
 
 			DIRECT_A1D_ELEM(sumweight_per_shell, i) += w;
 		}
@@ -461,8 +459,8 @@ void ParticlePolisher::writeStarFileRelativeWeights(FileName fn_star)
     for (int iframe = 0; iframe < XSIZE(perframe_bfactors)/3; iframe++ )
     {
 
-    	double bfactor = DIRECT_A1D_ELEM(perframe_bfactors, iframe * 3 + 0);
-    	double offset = DIRECT_A1D_ELEM(perframe_bfactors, iframe * 3 + 1);
+    	DOUBLE bfactor = DIRECT_A1D_ELEM(perframe_bfactors, iframe * 3 + 0);
+    	DOUBLE offset = DIRECT_A1D_ELEM(perframe_bfactors, iframe * 3 + 1);
 
     	MetaDataTable MDout;
 		std::string fn_table = "relative_weights_frame_" + integerToString(first_frame + iframe);
@@ -470,8 +468,8 @@ void ParticlePolisher::writeStarFileRelativeWeights(FileName fn_star)
 
 		for (int i = 1; i < ori_size/2; i++) // ignore origin
 		{
-			double res = (double)i / (ori_size * angpix); // resolution in 1/A
-			double w = exp( (bfactor / 4.)  * res * res  + offset);
+			DOUBLE res = (DOUBLE)i / (ori_size * angpix); // resolution in 1/A
+			DOUBLE w = exp( (bfactor / 4.)  * res * res  + offset);
 
 			w /= DIRECT_A1D_ELEM(sumweight_per_shell, i); // from absolute to relative weight
 			DIRECT_A1D_ELEM(cumulative_relweight_per_shell, i) += w;
@@ -502,7 +500,7 @@ void ParticlePolisher::calculateAverageAllSingleFrameReconstructions(int this_ha
 		return;
 	}
 
-	Image<double> Isum, Ione;
+	Image<DOUBLE> Isum, Ione;
 	for (long int this_frame = first_frame; this_frame <= last_frame; this_frame++)
 	{
     	FileName fn_vol;
@@ -553,13 +551,13 @@ void ParticlePolisher::calculateSingleFrameReconstruction(int this_frame, int th
 
 	CTF ctf;
 	std::string dum;
-	Matrix2D<double> A3D;
+	Matrix2D<DOUBLE> A3D;
 	MultidimArray<Complex > Faux, F2D, F2Dp, Fsub;
-	MultidimArray<double> Fweight, Fctf;
-	Image<double> img, vol;
+	MultidimArray<DOUBLE> Fweight, Fctf;
+	Image<DOUBLE> img, vol;
 	FourierTransformer transformer;
-	Matrix1D< double > trans(2);
-	double rot, tilt, psi;
+	DOUBLE xtrans, ytrans;
+	DOUBLE rot, tilt, psi;
 	int i_half;
 	long int i_frame;
 	FileName fn_img, fn_mic;
@@ -569,6 +567,9 @@ void ParticlePolisher::calculateSingleFrameReconstruction(int this_frame, int th
 		exp_model.MDimg.getValue(EMDL_PARTICLE_RANDOM_SUBSET, i_half);
 		exp_model.MDimg.getValue(EMDL_MICROGRAPH_NAME, fn_mic);
 		fn_mic.decompose(i_frame, dum);
+		// If we used --avg_movie_frame upon extraction, now count less
+		// e.g. instead of counting 4, 8, 12 count 1, 2, 3
+		i_frame /= step_frame;
 
 		// TODO!! Make a running average window here
 		if (ABS(i_frame - this_frame) <= frame_running_average/2 && i_half == this_half)
@@ -587,11 +588,11 @@ void ParticlePolisher::calculateSingleFrameReconstruction(int this_frame, int th
 			Euler_angles2matrix(rot, tilt, psi, A3D);
 
 			// Translations
-			trans.initZeros();
-			exp_model.MDimg.getValue( EMDL_ORIENT_ORIGIN_X, XX(trans));
-			exp_model.MDimg.getValue( EMDL_ORIENT_ORIGIN_Y, YY(trans));
-			if (ABS(XX(trans)) > 0. || ABS(YY(trans)) > 0. )
-				shiftImageInFourierTransform(F2D, F2D, image_size, trans );
+			xtrans=ytrans=0.;
+			exp_model.MDimg.getValue( EMDL_ORIENT_ORIGIN_X, xtrans);
+			exp_model.MDimg.getValue( EMDL_ORIENT_ORIGIN_Y, ytrans);
+			if (ABS(xtrans) > 0. || ABS(ytrans) > 0. )
+				shiftImageInFourierTransform(F2D, F2D, image_size, xtrans, ytrans );
 
 			// CTF
 			Fctf.resize(F2D);
@@ -613,14 +614,14 @@ void ParticlePolisher::calculateSingleFrameReconstruction(int this_frame, int th
 	}
 
 	// Now do the reconstruction
-	MultidimArray<double> dummy;
+	MultidimArray<DOUBLE> dummy;
 	backprojector.reconstruct(vol(), 10, false, 1., dummy, dummy, dummy, dummy);
 
 	vol.write(fn_vol);
 
 }
 
-void ParticlePolisher::calculateBfactorSingleFrameReconstruction(int this_frame, double &bfactor, double &offset, double &corr_coeff)
+void ParticlePolisher::calculateBfactorSingleFrameReconstruction(int this_frame, DOUBLE &bfactor, DOUBLE &offset, DOUBLE &corr_coeff)
 {
 
 	if (NZYXSIZE(Imask()) == 0)
@@ -639,8 +640,8 @@ void ParticlePolisher::calculateBfactorSingleFrameReconstruction(int this_frame,
 		fn_root_half.compose(fn_in.withoutExtension() + "_" + fn_out+"_frame",this_frame,"", 3);
 
 	FileName fn_half1, fn_half2;
-	Image<double> I1, I2;
-	MultidimArray<double> fsc_frame;
+	Image<DOUBLE> I1, I2;
+	MultidimArray<DOUBLE> fsc_frame;
 	I1.read(fn_root_half + "_half1_class001_unfil.mrc");
 	I2.read(fn_root_half + "_half2_class001_unfil.mrc");
 
@@ -673,12 +674,12 @@ void ParticlePolisher::calculateBfactorSingleFrameReconstruction(int this_frame,
 		std::vector<fit_point2D> guinier;
 		for (int i = 1; i < XSIZE(fsc_frame); i++) // ignore origin
 		{
-			double res = (double)i / (XSIZE(I1()) * angpix); // resolution in 1/A
-			double resang = 1. / res;
-			double res2 = res*res;
+			DOUBLE res = (DOUBLE)i / (XSIZE(I1()) * angpix); // resolution in 1/A
+			DOUBLE resang = 1. / res;
+			DOUBLE res2 = res*res;
 
-			double fsc_f = DIRECT_A1D_ELEM(fsc_frame, i);
-			double fsc_a = DIRECT_A1D_ELEM(fsc_average, i);
+			DOUBLE fsc_f = DIRECT_A1D_ELEM(fsc_frame, i);
+			DOUBLE fsc_a = DIRECT_A1D_ELEM(fsc_average, i);
 
 			if (resang < fit_minres && resang > perframe_highres && fsc_f < 1 && fsc_a < 1 && fsc_f > 0.143 && fsc_a > 0.143)
 			{
@@ -686,7 +687,7 @@ void ParticlePolisher::calculateBfactorSingleFrameReconstruction(int this_frame,
 				// I could have calculated the same using: ln(tau_f / tau_a)   = ln(tau_f) - ln(tau_a)
 				// where tau_f = sqrt (FSC_f / (1 - FSC_f)) and tau_a = sqrt (FSC_a / (1 - FSC_a))
 				// This is numerically identical
-				double logreltau = log( sqrt( (fsc_f - fsc_f * fsc_a) / (fsc_a - fsc_f * fsc_a) ) );
+				DOUBLE logreltau = log( sqrt( (fsc_f - fsc_f * fsc_a) / (fsc_a - fsc_f * fsc_a) ) );
 
 				onepoint.x = res2;
 				onepoint.y = logreltau;
@@ -725,7 +726,7 @@ void ParticlePolisher::polishParticlesAllMicrographs()
 	int my_nr_micrographs = exp_model.average_micrographs.size();
 	if (verb > 0)
 	{
-		std::cout << " + Write out polished particles for all micrographs ... " << std::endl;
+		std::cout << " + Write out polished particles for all " << my_nr_micrographs << " micrographs ... " << std::endl;
 		init_progress_bar(my_nr_micrographs);
 		barstep = XMIPP_MAX(1, my_nr_micrographs/ 60);
 	}
@@ -760,14 +761,13 @@ void ParticlePolisher::writeStarFilePolishedParticles()
 		{
 			long int ori_part_id = exp_model.average_micrographs[imic].ori_particles_id[ipar];
 			long int part_id = exp_model.ori_particles[ori_part_id].particles_id[first_frame - 1];
-			long int img_id = exp_model.getImageId(part_id, 0);
 
 			// Get the corresponding line from the input STAR file
-			MDshiny.addObject(exp_model.MDimg.getObject(img_id));
+			MDshiny.addObject(exp_model.MDimg.getObject(part_id));
 
 			// The orientations and offsets in this line of the metadatatable come only from a single frame!!!
 			// Use the ones stored in the prior instead.
-			double xoff, yoff, rot, tilt, psi;
+			DOUBLE xoff, yoff, rot, tilt, psi;
 			MDshiny.getValue(EMDL_ORIENT_ORIGIN_X_PRIOR, xoff);
 			MDshiny.getValue(EMDL_ORIENT_ORIGIN_Y_PRIOR, yoff);
 			MDshiny.getValue(EMDL_ORIENT_ROT_PRIOR, rot);
@@ -787,7 +787,7 @@ void ParticlePolisher::writeStarFilePolishedParticles()
 
 			// Also change this particle's image_name
 			FileName fn_part, fn_img;
-			exp_model.MDimg.getValue(EMDL_PARTICLE_ORI_NAME, fn_part, img_id);
+			exp_model.MDimg.getValue(EMDL_PARTICLE_ORI_NAME, fn_part, part_id);
 			fn_part = fn_part.withoutExtension();
 			std::string mic_name;
 			long int nr;
@@ -818,14 +818,14 @@ void ParticlePolisher::polishParticlesOneMicrograph(long int imic)
 
 	// Then read in all individual movie frames, apply frame x,y-movements as phase shifts and calculate polished (shiny) particles
 	// as average of the re-aligned frames
-	double x_off_p, y_off_p, x_off_prior_p, y_off_prior_p;
+	DOUBLE x_off_p, y_off_p, x_off_prior_p, y_off_prior_p;
 	FileName fn_img, fn_part;
-	Image<double> img;
+	Image<DOUBLE> img;
 	FourierTransformer transformer;
-	MultidimArray< Complex > Fimg, Fwsum;
-	MultidimArray<double> Fsumw;
-	Matrix1D <double> trans(2);
-	double all_minval = 99999., all_maxval = -99999., all_avg = 0., all_stddev = 0.;
+	MultidimArray<Complex > Fimg, Fwsum;
+	MultidimArray<DOUBLE> Fsumw;
+	DOUBLE xtrans, ytrans;
+	DOUBLE all_minval = 99999., all_maxval = -99999., all_avg = 0., all_stddev = 0.;
 
 	// Loop over all original_particles in this average_micrograph
 	for (long int ipar = 0; ipar < exp_model.average_micrographs[imic].ori_particles_id.size(); ipar++)
@@ -836,36 +836,35 @@ void ParticlePolisher::polishParticlesOneMicrograph(long int imic)
 		for (long int i_frame = first_frame; i_frame <= last_frame; i_frame++ )
 		{
 			long int part_id = exp_model.ori_particles[ori_part_id].particles_id[i_frame - 1]; // start counting frames at 0, not 1
-			long int img_id = exp_model.getImageId(part_id, 0);
 
-			exp_model.MDimg.getValue(EMDL_IMAGE_NAME, fn_img, img_id);
-			exp_model.MDimg.getValue(EMDL_PARTICLE_ORI_NAME, fn_part, img_id);
-			exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_X_PRIOR, x_off_prior_p, img_id);
-			exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_Y_PRIOR, y_off_prior_p, img_id);
+			exp_model.MDimg.getValue(EMDL_IMAGE_NAME, fn_img, part_id);
+			exp_model.MDimg.getValue(EMDL_PARTICLE_ORI_NAME, fn_part, part_id);
+			exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_X_PRIOR, x_off_prior_p, part_id);
+			exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_Y_PRIOR, y_off_prior_p, part_id);
 			if (fitting_mode != NO_FIT)
 			{
-				x_off_p = A2D_ELEM(fitted_movements, img_id, 0);
-				y_off_p = A2D_ELEM(fitted_movements, img_id, 1);
+				x_off_p = A2D_ELEM(fitted_movements, part_id, 0);
+				y_off_p = A2D_ELEM(fitted_movements, part_id, 1);
 			}
 			else
 			{
-				exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_X, x_off_p, img_id);
-				exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_Y, y_off_p, img_id);
+				exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_X, x_off_p, part_id);
+				exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_Y, y_off_p, part_id);
 			}
 
 			img.read(fn_img);
-			double ori_size= XSIZE(img());
+			DOUBLE ori_size= XSIZE(img());
 
 			// Get the image shifts relative to the prior
-			XX(trans) = x_off_p - x_off_prior_p;
-			YY(trans) = y_off_p - y_off_prior_p;
+			xtrans = x_off_p - x_off_prior_p;
+			ytrans = y_off_p - y_off_prior_p;
 
 #ifdef DEBUG
 			if (fn_part =="000001 at Particles/Micrographs/Falcon_2012_06_13-01_05_13_0_rh15particles.mrcs")
 			{
-				double xp, yp;
-				exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_X, xp, img_id);
-				exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_Y, yp, img_id);
+				DOUBLE xp, yp;
+				exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_X, xp, part_id);
+				exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_Y, yp, part_id);
 				std::cerr << " x_off_prior_p= " << x_off_prior_p << " x_off_p= " << x_off_p << " xp= " << xp << std::endl;
 				std::cerr << " y_off_prior_p= " << y_off_prior_p << " y_off_p= " << y_off_p << " yp= " << yp << std::endl;
 				std::cerr << " iframe= " << i_frame << " XX(trans)= " << XX(trans) << " YY(trans)= " << YY(trans) << std::endl;
@@ -881,18 +880,18 @@ void ParticlePolisher::polishParticlesOneMicrograph(long int imic)
 				Fsumw.initZeros(Fimg);
 			}
 
-			shiftImageInFourierTransform(Fimg, Fimg, ori_size, trans);
+			shiftImageInFourierTransform(Fimg, Fimg, ori_size, xtrans, ytrans);
 
 			// Apply (positive!!) B-factor weighting and store weighted sums
 			int iframe = i_frame - first_frame;
-			double bfactor = (do_weighting) ? DIRECT_A1D_ELEM(perframe_bfactors, 3*iframe + 0) : 0.;
-			double offset  = (do_weighting) ? DIRECT_A1D_ELEM(perframe_bfactors, 3*iframe + 1) : 0.;
+			DOUBLE bfactor = (do_weighting) ? DIRECT_A1D_ELEM(perframe_bfactors, 3*iframe + 0) : 0.;
+			DOUBLE offset  = (do_weighting) ? DIRECT_A1D_ELEM(perframe_bfactors, 3*iframe + 1) : 0.;
 			FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM2D(Fimg)
 			{
-				double res = sqrt((double)ip * ip + jp * jp) / (XSIZE(img()) * angpix); // get resolution in 1/Angstrom
+				DOUBLE res = sqrt((DOUBLE)ip * ip + jp * jp) / (XSIZE(img()) * angpix); // get resolution in 1/Angstrom
 				if (res <= 1. / (angpix * 2.) ) // Apply B-factor weighting until Nyquist
 				{
-					double w = exp( (bfactor / 4)  * res * res  + offset);
+					DOUBLE w = exp( (bfactor / 4)  * res * res  + offset);
 					DIRECT_A2D_ELEM(Fwsum, i, j) += w * DIRECT_A2D_ELEM(Fimg, i, j);
 					DIRECT_A2D_ELEM(Fsumw, i, j) += w;
 				}
@@ -908,10 +907,10 @@ void ParticlePolisher::polishParticlesOneMicrograph(long int imic)
 		transformer.inverseFourierTransform(Fwsum, img());
 
 		if (do_normalise)
-			normalise(img, bg_radius, white_dust_stddev, black_dust_stddev);
+			normalise(img, bg_radius, white_dust_stddev, black_dust_stddev, do_ramp);
 
 		// Calculate statistics for the (normalised?) particle
-		double minval, maxval, avgval, stddev;
+		DOUBLE minval, maxval, avgval, stddev;
 		img().computeStats(avgval, stddev, minval, maxval);
 
 		// Keep track of overall statistics for all particles in this field-of-view
@@ -998,8 +997,8 @@ void ParticlePolisher::reconstructShinyParticlesAndFscWeight(int ipass)
 	if (verb > 0)
 		std::cout << " + Setting Fourier transforms of the two shiny half-reconstructions ..." << std::endl;
 
-	MultidimArray<double> dum;
-	Image<double> refvol;
+	MultidimArray<DOUBLE> dum;
+	Image<DOUBLE> refvol;
 	FileName fn_vol;
 	fn_vol = fn_in.withoutExtension() + "_" + fn_out + "_half1_class001_unfil.mrc";
 	refvol.read(fn_vol);
@@ -1007,6 +1006,7 @@ void ParticlePolisher::reconstructShinyParticlesAndFscWeight(int ipass)
 	PPrefvol_half1.padding_factor = 2;
 	PPrefvol_half1.interpolator = TRILINEAR;
 	PPrefvol_half1.r_min_nn = 10;
+	PPrefvol_half1.data_dim = 2;
 	PPrefvol_half1.computeFourierTransformMap(refvol(), dum);
 	fn_vol = fn_in.withoutExtension() + "_" + fn_out + "_half2_class001_unfil.mrc";
 	refvol.read(fn_vol);
@@ -1014,6 +1014,7 @@ void ParticlePolisher::reconstructShinyParticlesAndFscWeight(int ipass)
 	PPrefvol_half2.padding_factor = 2;
 	PPrefvol_half2.interpolator = TRILINEAR;
 	PPrefvol_half2.r_min_nn = 10;
+	PPrefvol_half2.data_dim = 2;
 	PPrefvol_half2.computeFourierTransformMap(refvol(), dum);
 
 }
@@ -1039,13 +1040,13 @@ void ParticlePolisher::reconstructShinyParticlesOneHalf(int this_half)
 
 	CTF ctf;
 	std::string dum;
-	Matrix2D<double> A3D;
+	Matrix2D<DOUBLE> A3D;
 	MultidimArray<Complex > Faux, F2D, Fsub;
-	MultidimArray<double> Fweight, Fctf;
-	Image<double> img, vol;
+	MultidimArray<DOUBLE> Fweight, Fctf;
+	Image<DOUBLE> img, vol;
 	FourierTransformer transformer;
-	Matrix1D< double > trans(2);
-	double rot, tilt, psi;
+	DOUBLE xtrans, ytrans;
+	DOUBLE rot, tilt, psi;
 	int i_half;
 	long int i_frame;
 	FileName fn_img;
@@ -1069,11 +1070,11 @@ void ParticlePolisher::reconstructShinyParticlesOneHalf(int this_half)
 			Euler_angles2matrix(rot, tilt, psi, A3D);
 
 			// Translations
-			trans.initZeros();
-			exp_model.MDimg.getValue( EMDL_ORIENT_ORIGIN_X, XX(trans));
-			exp_model.MDimg.getValue( EMDL_ORIENT_ORIGIN_Y, YY(trans));
-			if (ABS(XX(trans)) > 0. || ABS(YY(trans)) > 0. )
-				shiftImageInFourierTransform(F2D, F2D, image_size, trans );
+			xtrans = ytrans = 0.;
+			exp_model.MDimg.getValue( EMDL_ORIENT_ORIGIN_X, xtrans);
+			exp_model.MDimg.getValue( EMDL_ORIENT_ORIGIN_Y, ytrans);
+			if (ABS(xtrans) > 0. || ABS(ytrans) > 0. )
+				shiftImageInFourierTransform(F2D, F2D, image_size, xtrans, ytrans );
 
 			// CTF
 			Fctf.resize(F2D);
@@ -1095,7 +1096,7 @@ void ParticlePolisher::reconstructShinyParticlesOneHalf(int this_half)
 	}
 
 	// Now do the reconstruction
-	MultidimArray<double> dummy;
+	MultidimArray<DOUBLE> dummy;
 	backprojector.reconstruct(vol(), 10, false, 1., dummy, dummy, dummy, dummy);
 
 	vol.write(fn_vol);
@@ -1190,9 +1191,9 @@ void ParticlePolisher::initialiseSquaredDifferenceVectors()
 	if (beamtilt_max > 0.)
 	{
 		int n_steps = CEIL(beamtilt_max / beamtilt_step);
-		for (double tilt_y = -n_steps*beamtilt_step; tilt_y <= n_steps*beamtilt_step; tilt_y += beamtilt_step)
+		for (DOUBLE tilt_y = -n_steps*beamtilt_step; tilt_y <= n_steps*beamtilt_step; tilt_y += beamtilt_step)
 		{
-			for (double tilt_x = -n_steps*beamtilt_step; tilt_x <= n_steps*beamtilt_step; tilt_x += beamtilt_step)
+			for (DOUBLE tilt_x = -n_steps*beamtilt_step; tilt_x <= n_steps*beamtilt_step; tilt_x += beamtilt_step)
 			{
 				if (sqrt(tilt_y*tilt_y + tilt_x*tilt_x) <= beamtilt_max)
 				{
@@ -1220,31 +1221,31 @@ void ParticlePolisher::applyOptimisedBeamTiltsAndDefocus()
 		int n_steps = CEIL(beamtilt_max / beamtilt_step);
 
 		best_beamtilts.clear();
-		Matrix1D<double> my_tilts(2);
+		Matrix1D<DOUBLE> my_tilts(2);
 		for (int igroup = 0; igroup < fn_beamtilt_groups.size(); igroup++)
 		{
-			double mindiff2 = 99.e99;
-			double best_tilt_x, best_tilt_y;
+			DOUBLE mindiff2 = 99.e99;
+			DOUBLE best_tilt_x, best_tilt_y;
 			if (verb > 0)
 				std::cout << " + Beamtilt group " << fn_beamtilt_groups[igroup] << std::endl;
 			int n_tilt= 0;
-			for (double tilt_y = -n_steps*beamtilt_step; tilt_y <= n_steps*beamtilt_step; tilt_y += beamtilt_step)
+			for (DOUBLE tilt_y = -n_steps*beamtilt_step; tilt_y <= n_steps*beamtilt_step; tilt_y += beamtilt_step)
 			{
-				for (double tilt_x = -n_steps*beamtilt_step; tilt_x <= n_steps*beamtilt_step; tilt_x += beamtilt_step)
+				for (DOUBLE tilt_x = -n_steps*beamtilt_step; tilt_x <= n_steps*beamtilt_step; tilt_x += beamtilt_step)
 				{
 					if (sqrt(tilt_y*tilt_y + tilt_x*tilt_x) <= beamtilt_max)
 					{
-						double diff2 = DIRECT_A2D_ELEM(diff2_beamtilt, igroup, n_tilt);
+						DOUBLE diff2 = DIRECT_A2D_ELEM(diff2_beamtilt, igroup, n_tilt);
 						if (verb > 1)
 							std::cout << " + tilt_x = " << tilt_x << " + tilt_y = " << tilt_y << " diff2= " << diff2 << std::endl;
-	#ifdef DEBUG_TILT
+#ifdef DEBUG_TILT
 
 						if (verb > 0)
 							std::cerr << " igroup= " << igroup << " n_tilt= " << n_tilt
 							<< " DIRECT_A2D_ELEM(diff2_beamtilt, igroup, n_tilt)= " << DIRECT_A2D_ELEM(diff2_beamtilt, igroup, n_tilt)
 							<< " diff2= " << diff2 << " mindiff2= " << mindiff2
 							<< std::endl;
-	#endif
+#endif
 						if (diff2 <= mindiff2)
 						{
 							best_tilt_x = tilt_x;
@@ -1269,22 +1270,22 @@ void ParticlePolisher::applyOptimisedBeamTiltsAndDefocus()
 	for (long int imic = 0; imic < exp_model.micrographs.size(); imic++)
 	{
 
-		double best_defocus_shift = (defocus_shift_max > 0.) ? DIRECT_A1D_ELEM(defocus_shift_allmics, imic) : 0.;
-		for (long int iimg = 0; iimg < exp_model.micrographs[imic].images.size(); iimg++)
+		DOUBLE best_defocus_shift = (defocus_shift_max > 0.) ? DIRECT_A1D_ELEM(defocus_shift_allmics, imic) : 0.;
+		for (long int ipart = 0; ipart < exp_model.micrographs[imic].particle_ids.size(); ipart++)
     	{
-    		long int img_id = exp_model.micrographs[imic].images[iimg].id;
+			long int part_id = exp_model.micrographs[imic].particle_ids[ipart];
 
     		// Set the optimised beamtilts in the MetadataTable
     		if (beamtilt_max > 0.)
     		{
 				// First get which beamtilt group this micrograph comes from
-				if (iimg == 0)
+				if (ipart == 0)
 				{
 					FileName fn_group;
 					if (exp_model.MDimg.containsLabel(EMDL_IMAGE_BEAMTILT_GROUP))
-						exp_model.MDimg.getValue(EMDL_IMAGE_BEAMTILT_GROUP, fn_group, img_id);
+						exp_model.MDimg.getValue(EMDL_IMAGE_BEAMTILT_GROUP, fn_group, part_id);
 					else
-						exp_model.MDimg.getValue(EMDL_MICROGRAPH_NAME, fn_group, img_id);
+						exp_model.MDimg.getValue(EMDL_MICROGRAPH_NAME, fn_group, part_id);
 					bool found = false;
 					for (int igroup = 0; igroup < fn_beamtilt_groups.size(); igroup++)
 					{
@@ -1301,8 +1302,8 @@ void ParticlePolisher::applyOptimisedBeamTiltsAndDefocus()
 				}
 
 				// Then set the corresponding beamtilt values for each particles
-				exp_model.MDimg.setValue(EMDL_IMAGE_BEAMTILT_X, XX(best_beamtilts[i_group]), img_id);
-				exp_model.MDimg.setValue(EMDL_IMAGE_BEAMTILT_Y, YY(best_beamtilts[i_group]), img_id);
+				exp_model.MDimg.setValue(EMDL_IMAGE_BEAMTILT_X, XX(best_beamtilts[i_group]), part_id);
+				exp_model.MDimg.setValue(EMDL_IMAGE_BEAMTILT_Y, YY(best_beamtilts[i_group]), part_id);
 
     		}
 
@@ -1310,14 +1311,14 @@ void ParticlePolisher::applyOptimisedBeamTiltsAndDefocus()
 			if (defocus_shift_max > 0.)
 			{
 
-				if (iimg == 0 && verb > 0)
+				if (ipart == 0 && verb > 0)
 					std::cout << " + Micrograph " << exp_model.micrographs[imic].name << " defocus_shift= " << best_defocus_shift << std::endl;
 
-				double ori_defocusU, ori_defocusV;
-				exp_model.MDimg.getValue(EMDL_CTF_DEFOCUSU, ori_defocusU, img_id);
-				exp_model.MDimg.getValue(EMDL_CTF_DEFOCUSV, ori_defocusV, img_id);
-				exp_model.MDimg.setValue(EMDL_CTF_DEFOCUSU, ori_defocusU + best_defocus_shift, img_id);
-				exp_model.MDimg.setValue(EMDL_CTF_DEFOCUSV, ori_defocusV + best_defocus_shift, img_id);
+				DOUBLE ori_defocusU, ori_defocusV;
+				exp_model.MDimg.getValue(EMDL_CTF_DEFOCUSU, ori_defocusU, part_id);
+				exp_model.MDimg.getValue(EMDL_CTF_DEFOCUSV, ori_defocusV, part_id);
+				exp_model.MDimg.setValue(EMDL_CTF_DEFOCUSU, ori_defocusU + best_defocus_shift, part_id);
+				exp_model.MDimg.setValue(EMDL_CTF_DEFOCUSV, ori_defocusV + best_defocus_shift, part_id);
 			}
     	}
 	}
@@ -1334,40 +1335,39 @@ void ParticlePolisher::optimiseBeamTiltAndDefocusOneMicrograph(int imic)
 	exp_model.MDexp.getValue(EMDL_IMAGE_SIZE, image_size);
 
 	CTF ctf;
-	Matrix2D<double> A3D;
+	Matrix2D<DOUBLE> A3D;
 	MultidimArray<Complex > F2D, F2Dtilt, Fref;
-	MultidimArray<double> Fctf;
-	Image<double> img;
+	MultidimArray<DOUBLE> Fctf;
+	Image<DOUBLE> img;
 	FourierTransformer transformer;
-	Matrix1D< double > trans(2);
-	double rot, tilt, psi;
+	DOUBLE xtrans, ytrans;
+	DOUBLE rot, tilt, psi;
 	FileName fn_img;
 	int i_group = -1, my_half = 0;
 
-	double xsize = angpix * PPrefvol_half1.ori_size;
+	DOUBLE xsize = angpix * PPrefvol_half1.ori_size;
 
 	// Weighted squared-differences for all defocusses
 	int nr_sampled_defocus_shifts;
-	MultidimArray<double> wdiff2_defocus;
+	MultidimArray<DOUBLE> wdiff2_defocus;
 	if (defocus_shift_max > 0.)
 	{
 		nr_sampled_defocus_shifts = CEIL(defocus_shift_max / defocus_shift_step);
 		wdiff2_defocus.initZeros(2*nr_sampled_defocus_shifts + 1);
 	}
 
-	for (long int iimg = 0; iimg < exp_model.micrographs[imic].images.size(); iimg++)
+	for (long int ipart = 0; ipart < exp_model.micrographs[imic].particle_ids.size(); ipart++)
 	{
-
-		long int img_id = exp_model.micrographs[imic].images[iimg].id;
+		long int part_id = exp_model.micrographs[imic].particle_ids[ipart];
 
 		// Get which beamtilt group this micrograph comes from
-		if (iimg == 0)
+		if (ipart == 0)
 		{
 			FileName fn_group;
 			if (exp_model.MDimg.containsLabel(EMDL_IMAGE_BEAMTILT_GROUP))
-				exp_model.MDimg.getValue(EMDL_IMAGE_BEAMTILT_GROUP, fn_group, img_id);
+				exp_model.MDimg.getValue(EMDL_IMAGE_BEAMTILT_GROUP, fn_group, part_id);
 			else
-				exp_model.MDimg.getValue(EMDL_MICROGRAPH_NAME, fn_group, img_id);
+				exp_model.MDimg.getValue(EMDL_MICROGRAPH_NAME, fn_group, part_id);
 			bool found = false;
 			for (int igroup = 0; igroup < fn_beamtilt_groups.size(); igroup++)
 			{
@@ -1384,19 +1384,19 @@ void ParticlePolisher::optimiseBeamTiltAndDefocusOneMicrograph(int imic)
 		}
 
 
-		exp_model.MDimg.getValue(EMDL_IMAGE_NAME, fn_img, img_id);
+		exp_model.MDimg.getValue(EMDL_IMAGE_NAME, fn_img, part_id);
 		img.read(fn_img);
 		CenterFFT(img(), true);
 		transformer.FourierTransform(img(), F2D);
 
 		// Which half do I belong to?
-		exp_model.MDimg.getValue(EMDL_PARTICLE_RANDOM_SUBSET, my_half, img_id);
+		exp_model.MDimg.getValue(EMDL_PARTICLE_RANDOM_SUBSET, my_half, part_id);
 
 		// Use the prior-angles, as these were determined from the average particles
 		// The individual-frame-determined angles would be too noisy....
-		exp_model.MDimg.getValue(EMDL_ORIENT_ROT, rot, img_id);
-		exp_model.MDimg.getValue(EMDL_ORIENT_TILT, tilt, img_id);
-		exp_model.MDimg.getValue(EMDL_ORIENT_PSI, psi, img_id);
+		exp_model.MDimg.getValue(EMDL_ORIENT_ROT, rot, part_id);
+		exp_model.MDimg.getValue(EMDL_ORIENT_TILT, tilt, part_id);
+		exp_model.MDimg.getValue(EMDL_ORIENT_PSI, psi, part_id);
 		Euler_angles2matrix(rot, tilt, psi, A3D);
 
 		// Get the reference projection
@@ -1409,31 +1409,31 @@ void ParticlePolisher::optimiseBeamTiltAndDefocusOneMicrograph(int imic)
 			REPORT_ERROR("ERROR unrecognised random subset, not 1 or 2...");
 
 		// Shift the experimental image
-		trans.initZeros();
-		exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_X, XX(trans), img_id);
-		exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_Y, YY(trans), img_id);
-		if (ABS(XX(trans)) > 0. || ABS(YY(trans)) > 0. )
-			shiftImageInFourierTransform(F2D, F2D, image_size, trans );
+		xtrans = ytrans = 0.;
+		exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_X, xtrans, part_id);
+		exp_model.MDimg.getValue(EMDL_ORIENT_ORIGIN_Y, ytrans, part_id);
+		if (ABS(xtrans) > 0. || ABS(ytrans) > 0. )
+			shiftImageInFourierTransform(F2D, F2D, image_size, xtrans, ytrans );
 
 		// apply CTF to the reference
 		if (do_ctf)
 		{
 
 			Fctf.resize(F2D);
-			ctf.read(exp_model.MDimg, exp_model.MDimg, img_id);
+			ctf.read(exp_model.MDimg, exp_model.MDimg, part_id);
 
 			// Store the original values of defocusU and defocusV
-			double ori_defocusU = ctf.DeltafU;
-			double ori_defocusV = ctf.DeltafV;
-			double best_defocus_shift;
-			double mindiff_thispart = 99.e99;
+			DOUBLE ori_defocusU = ctf.DeltafU;
+			DOUBLE ori_defocusV = ctf.DeltafV;
+			DOUBLE best_defocus_shift;
+			DOUBLE mindiff_thispart = 99.e99;
 			// Optimise per-particle CTF defocus
 			// For now only non-anisotropically
 			if (defocus_shift_max > 0.)
 			{
 
 				int n_defocus = 0;
-				for (double defocus_shift = -nr_sampled_defocus_shifts*defocus_shift_step;
+				for (DOUBLE defocus_shift = -nr_sampled_defocus_shifts*defocus_shift_step;
 						defocus_shift <= nr_sampled_defocus_shifts*defocus_shift_step;
 						defocus_shift += defocus_shift_step)
 				{
@@ -1444,14 +1444,14 @@ void ParticlePolisher::optimiseBeamTiltAndDefocusOneMicrograph(int imic)
 					ctf.getFftwImage(Fctf, image_size, image_size, angpix, ctf_phase_flipped, only_flip_phases, intact_ctf_first_peak, true);
 
 					// Calculate squared difference with the image
-					double diff2 = 0.;
+					DOUBLE diff2 = 0.;
 					FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM2D(F2D)
 					{
-						double res = xsize/sqrt((double)(ip * ip + jp * jp)); // get resolution in 1/pixel
+						DOUBLE res = xsize/sqrt((DOUBLE)(ip * ip + jp * jp)); // get resolution in 1/pixel
 						if (res <= minres_beamtilt && res >= maxres_model)
 				    	{
-							double diff_real = DIRECT_A2D_ELEM(Fctf, i, j) * (DIRECT_A2D_ELEM(Fref, i, j)).real - (DIRECT_A2D_ELEM(F2D, i, j)).real;
-							double diff_imag = DIRECT_A2D_ELEM(Fctf, i, j) * (DIRECT_A2D_ELEM(Fref, i, j)).imag - (DIRECT_A2D_ELEM(F2D, i, j)).imag;
+							DOUBLE diff_real = DIRECT_A2D_ELEM(Fctf, i, j) * (DIRECT_A2D_ELEM(Fref, i, j)).real - (DIRECT_A2D_ELEM(F2D, i, j)).real;
+							DOUBLE diff_imag = DIRECT_A2D_ELEM(Fctf, i, j) * (DIRECT_A2D_ELEM(Fref, i, j)).imag - (DIRECT_A2D_ELEM(F2D, i, j)).imag;
 							diff2 += (diff_real * diff_real + diff_imag * diff_imag);
 				    	}
 					}
@@ -1469,8 +1469,8 @@ void ParticlePolisher::optimiseBeamTiltAndDefocusOneMicrograph(int imic)
 
 				// Set best defocus for each individual particle...
 				// TODO!!! This only works in sequential version!!!
-				//exp_model.MDimg.setValue(EMDL_CTF_DEFOCUSU, ori_defocusU + best_defocus_shift, img_id);
-				//exp_model.MDimg.setValue(EMDL_CTF_DEFOCUSV, ori_defocusV + best_defocus_shift, img_id);
+				//exp_model.MDimg.setValue(EMDL_CTF_DEFOCUSU, ori_defocusU + best_defocus_shift, part_id);
+				//exp_model.MDimg.setValue(EMDL_CTF_DEFOCUSV, ori_defocusV + best_defocus_shift, part_id);
 
 				// Re-set the original defocus values in the ctf object
 				ctf.DeltafU = ori_defocusU;
@@ -1498,26 +1498,26 @@ void ParticlePolisher::optimiseBeamTiltAndDefocusOneMicrograph(int imic)
 			// Loop over all beam tilts
 			int n_tilts = 0;
 			int n_steps = CEIL(beamtilt_max / beamtilt_step);
-			for (double tilt_y = -n_steps*beamtilt_step; tilt_y <= n_steps*beamtilt_step; tilt_y += beamtilt_step)
+			for (DOUBLE tilt_y = -n_steps*beamtilt_step; tilt_y <= n_steps*beamtilt_step; tilt_y += beamtilt_step)
 			{
-				for (double tilt_x = -n_steps*beamtilt_step; tilt_x <= n_steps*beamtilt_step; tilt_x += beamtilt_step)
+				for (DOUBLE tilt_x = -n_steps*beamtilt_step; tilt_x <= n_steps*beamtilt_step; tilt_x += beamtilt_step)
 				{
 					if (sqrt(tilt_y*tilt_y + tilt_x*tilt_x) <= beamtilt_max)
 					{
 						// Now calculate the squared differences, taking the beamtilt into account
 						applyBeamTilt(F2D, F2Dtilt, tilt_x, tilt_y, ctf.lambda, ctf.Cs, angpix, image_size);
 
-						double diff2 = 0.;
-						double ndiff = 0.;
+						DOUBLE diff2 = 0.;
+						DOUBLE ndiff = 0.;
 						FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM2D(F2Dtilt)
 						{
 							// Store amplitude-weighted phase difference and sum of amplitude-weights
-							double res = xsize/sqrt((double)(ip * ip + jp * jp)); // get resolution in 1/pixel
+							DOUBLE res = xsize/sqrt((DOUBLE)(ip * ip + jp * jp)); // get resolution in 1/pixel
 							if (res <= minres_beamtilt && res >= maxres_model)
 							{
 
-								double diff_real = (DIRECT_A2D_ELEM(Fref, i, j)).real - (DIRECT_A2D_ELEM(F2Dtilt, i, j)).real;
-								double diff_imag = (DIRECT_A2D_ELEM(Fref, i, j)).imag - (DIRECT_A2D_ELEM(F2Dtilt, i, j)).imag;
+								DOUBLE diff_real = (DIRECT_A2D_ELEM(Fref, i, j)).real - (DIRECT_A2D_ELEM(F2Dtilt, i, j)).real;
+								DOUBLE diff_imag = (DIRECT_A2D_ELEM(Fref, i, j)).imag - (DIRECT_A2D_ELEM(F2Dtilt, i, j)).imag;
 								diff2 += (diff_real * diff_real + diff_imag * diff_imag);
 								ndiff += 1.;
 							}
@@ -1547,13 +1547,13 @@ void ParticlePolisher::optimiseBeamTiltAndDefocusOneMicrograph(int imic)
 	// Set optimal defocus shift averaged over all particles in this micrograph:
 	if (defocus_shift_max > 0.)
 	{
-		double mindiff2=99.e99, best_defocus_shift;
+		DOUBLE mindiff2=99.e99, best_defocus_shift;
 		int n_defocus = 0;
-		for (double defocus_shift = -nr_sampled_defocus_shifts*defocus_shift_step;
+		for (DOUBLE defocus_shift = -nr_sampled_defocus_shifts*defocus_shift_step;
 				defocus_shift <= nr_sampled_defocus_shifts*defocus_shift_step;
 				defocus_shift += defocus_shift_step)
 		{
-			double diff2 = DIRECT_A1D_ELEM(wdiff2_defocus, n_defocus);
+			DOUBLE diff2 = DIRECT_A1D_ELEM(wdiff2_defocus, n_defocus);
 			//std::cerr << std::setprecision(10) << " imic= " << imic << " defocus_shift= " << defocus_shift << " diff2= " << diff2 << std::endl;
 			if (diff2 < mindiff2)
 			{
@@ -1564,21 +1564,20 @@ void ParticlePolisher::optimiseBeamTiltAndDefocusOneMicrograph(int imic)
 		}
 		DIRECT_A1D_ELEM(defocus_shift_allmics, imic) = best_defocus_shift;
 
-		//exp_model.MDimg.setValue(EMDL_CTF_DEFOCUSU, ori_defocusU + best_defocus_shift, img_id);
-		//exp_model.MDimg.setValue(EMDL_CTF_DEFOCUSV, ori_defocusV + best_defocus_shift, img_id);
+		//exp_model.MDimg.setValue(EMDL_CTF_DEFOCUSU, ori_defocusU + best_defocus_shift, part_id);
+		//exp_model.MDimg.setValue(EMDL_CTF_DEFOCUSV, ori_defocusV + best_defocus_shift, part_id);
 		// Set best defocus for all particles on this micrograph
 		// TODO: this only works in sequential version!!!
-		for (long int iimg = 0; iimg < exp_model.micrographs[imic].images.size(); iimg++)
+		for (long int ipart = 0; ipart < exp_model.micrographs[imic].particle_ids.size(); ipart++)
 		{
-
-			long int img_id = exp_model.micrographs[imic].images[iimg].id;
-			double ori_defocusU, ori_defocusV;
-			exp_model.MDimg.getValue(EMDL_CTF_DEFOCUSU, ori_defocusU, img_id);
-			exp_model.MDimg.getValue(EMDL_CTF_DEFOCUSV, ori_defocusV, img_id);
+			long int part_id = exp_model.micrographs[imic].particle_ids[ipart];
+			DOUBLE ori_defocusU, ori_defocusV;
+			exp_model.MDimg.getValue(EMDL_CTF_DEFOCUSU, ori_defocusU, part_id);
+			exp_model.MDimg.getValue(EMDL_CTF_DEFOCUSV, ori_defocusV, part_id);
 
 
-			exp_model.MDimg.setValue(EMDL_CTF_DEFOCUSU, ori_defocusU + best_defocus_shift, img_id);
-			exp_model.MDimg.setValue(EMDL_CTF_DEFOCUSV, ori_defocusV + best_defocus_shift, img_id);
+			exp_model.MDimg.setValue(EMDL_CTF_DEFOCUSU, ori_defocusU + best_defocus_shift, part_id);
+			exp_model.MDimg.setValue(EMDL_CTF_DEFOCUSV, ori_defocusV + best_defocus_shift, part_id);
 		}
 
 	}
@@ -1592,8 +1591,8 @@ void ParticlePolisher::run()
 		fitMovementsAllMicrographs();
 
 	// Perform single-frame reconstructions and estimate dose-dependent B-factors
-	if (do_weighting)
-		calculateAllSingleFrameReconstructionsAndBfactors();
+        if (do_weighting)
+	     calculateAllSingleFrameReconstructionsAndBfactors();
 
 	// Write out the intermediately polished particles
 	polishParticlesAllMicrographs();
diff --git a/src/particle_polisher.h b/src/particle_polisher.h
index cd7925c..24064c5 100644
--- a/src/particle_polisher.h
+++ b/src/particle_polisher.h
@@ -53,17 +53,17 @@ public:
 	Experiment exp_model;
 
 	// Standard deviation for a Gaussian-weight on the distance between particles in the micrograph
-	double sigma_neighbour_distance;
+	DOUBLE sigma_neighbour_distance;
 
 	// Maximum resolution in pre-frame reconstructions
-	double perframe_highres;
+	DOUBLE perframe_highres;
 
 	// Flag to indicate all calculations have to be repeated from scratch
 	// if false, then intermediate files are re-read from disc and earlier calculations are skipped
 	bool do_start_all_over;
 
-	// First and last frame numbers to include in the average
-	int first_frame, last_frame;
+	// First and last frame numbers to include in the average, Also step if one had used --avg_movie_frames in the extraction
+	int first_frame, last_frame, step_frame;
 
 	// Which fitting mode (lienar/logarithmic/nofit)
 	int fitting_mode;
@@ -76,7 +76,7 @@ public:
 	bool do_ctf, ctf_phase_flipped, only_flip_phases, intact_ctf_first_peak;
 
 	// Pixel size (for B-factors)
-	double angpix;
+	DOUBLE angpix;
 
 	// Original image size
 	int ori_size;
@@ -85,22 +85,22 @@ public:
 	bool do_weighting;
 
 	// Minimum resolution (in Angstrom) for fitting of B-factor in Guinier plot
-	double fit_minres;
+	DOUBLE fit_minres;
 
 	// Width of a running average window for the single-frame reconstructions
 	int frame_running_average;
 
 	// Vector with the B-factors for all individual frames
-	MultidimArray<double> perframe_bfactors;
+	MultidimArray<DOUBLE> perframe_bfactors;
 
 	// Fitted movement coordinates for all input images
-	MultidimArray<double> fitted_movements;
+	MultidimArray<DOUBLE> fitted_movements;
 
 	// Image with the mask (used for relative weighting of each frame)
-	Image<double> Imask;
+	Image<DOUBLE> Imask;
 
 	// FSC curve of the masked, averages of all single-frame reconstructions
-	MultidimArray<double> fsc_average;
+	MultidimArray<DOUBLE> fsc_average;
 
 	// Metadatatable with the information from the polished particles
 	MetaDataTable MDshiny;
@@ -115,17 +115,20 @@ public:
 	// Normalise the polished particles?
 	bool do_normalise;
 
+	// Subtract a ramping background in the normalisation?
+	bool do_ramp;
+
 	// Radius of the background-circle for noise normalisation (in pixels)
 	int bg_radius;
 
 	// Sigma-levels for dust removal
-	double white_dust_stddev, black_dust_stddev;
+	DOUBLE white_dust_stddev, black_dust_stddev;
 
 	// Maximum useful resolution in the reconstruction
-	double maxres_model;
+	DOUBLE maxres_model;
 
 	// Maximum beam tilt to analyse, and step-size to sample in X and Y
-	double beamtilt_max, beamtilt_step;
+	DOUBLE beamtilt_max, beamtilt_step;
 	// Number of sampled beamtilts
 	int nr_sampled_beam_tilts;
 
@@ -133,19 +136,19 @@ public:
 	std::vector<FileName> fn_beamtilt_groups;
 
 	// Minimum resolution to take beamtilt into account
-	double minres_beamtilt;
+	DOUBLE minres_beamtilt;
 
 	// Weighted squared-differences for all beamtilts
-	MultidimArray<double> diff2_beamtilt;
+	MultidimArray<DOUBLE> diff2_beamtilt;
 
 	// Weighted squared-differences for all defocus values
-	MultidimArray<double> defocus_shift_allmics;
+	MultidimArray<DOUBLE> defocus_shift_allmics;
 
 	// Optimal beamtilts for each data set
-	std::vector<Matrix1D<double> > best_beamtilts;
+	std::vector<Matrix1D<DOUBLE> > best_beamtilts;
 
 	// Per-particle CTF optimisation
-	double defocus_shift_max, defocus_shift_step;
+	DOUBLE defocus_shift_max, defocus_shift_step;
 
 
 public:
@@ -184,7 +187,7 @@ public:
 	void postProcessSingleFrameReconstruction(int this_frame);
 
 	// Calculate the B-factors for a single-frame reconstruction
-	void calculateBfactorSingleFrameReconstruction(int this_frame, double &bfactor, double &offset, double &corr_coeff);
+	void calculateBfactorSingleFrameReconstruction(int this_frame, DOUBLE &bfactor, DOUBLE &offset, DOUBLE &corr_coeff);
 
 	// Calculate the average of all single-frame rconstructions (for a given half)
 	void calculateAverageAllSingleFrameReconstructions(int ihalf);
diff --git a/src/particle_polisher_mpi.cpp b/src/particle_polisher_mpi.cpp
index 477cd00..21f2cd3 100644
--- a/src/particle_polisher_mpi.cpp
+++ b/src/particle_polisher_mpi.cpp
@@ -77,20 +77,19 @@ void ParticlePolisherMpi::fitMovementsAllMicrographs()
 	}
 
 	// Combine results from all nodes
-	MultidimArray<double> allnodes_fitted_movements;
+	MultidimArray<DOUBLE> allnodes_fitted_movements;
 	allnodes_fitted_movements.resize(fitted_movements);
-	MPI_Allreduce(MULTIDIM_ARRAY(fitted_movements), MULTIDIM_ARRAY(allnodes_fitted_movements), MULTIDIM_SIZE(fitted_movements), MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
+	MPI_Allreduce(MULTIDIM_ARRAY(fitted_movements), MULTIDIM_ARRAY(allnodes_fitted_movements), MULTIDIM_SIZE(fitted_movements), MY_MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
 	fitted_movements = allnodes_fitted_movements;
 
     // Set the fitted movements in the xoff and yoff columns of the exp_model.MDimg
-    for (int ipart = 0; ipart < exp_model.numberOfParticles(); ipart++)
+    for (long int ipart = 0; ipart < exp_model.numberOfParticles(); ipart++)
 	{
 		long int part_id = exp_model.particles[ipart].id;
-		long int img_id = exp_model.getImageId(part_id, 0);
-		double xoff = DIRECT_A2D_ELEM(fitted_movements, img_id, 0);
-		double yoff = DIRECT_A2D_ELEM(fitted_movements, img_id, 1);
-		exp_model.MDimg.setValue(EMDL_ORIENT_ORIGIN_X, xoff, img_id);
-		exp_model.MDimg.setValue(EMDL_ORIENT_ORIGIN_Y, yoff, img_id);
+		DOUBLE xoff = DIRECT_A2D_ELEM(fitted_movements, part_id, 0);
+		DOUBLE yoff = DIRECT_A2D_ELEM(fitted_movements, part_id, 1);
+		exp_model.MDimg.setValue(EMDL_ORIENT_ORIGIN_X, xoff, part_id);
+		exp_model.MDimg.setValue(EMDL_ORIENT_ORIGIN_Y, yoff, part_id);
 	}
 
     if (node->isMaster())
@@ -115,7 +114,7 @@ void ParticlePolisherMpi::calculateAllSingleFrameReconstructionsAndBfactors()
 		return;
 	}
 
-	double bfactor, offset, corr_coeff;
+	DOUBLE bfactor, offset, corr_coeff;
 
 	int total_nr_frames = last_frame - first_frame + 1;
 	long int my_first_frame, my_last_frame, my_nr_frames;
@@ -189,9 +188,9 @@ void ParticlePolisherMpi::calculateAllSingleFrameReconstructionsAndBfactors()
 	}
 
 	// Combine results from all nodes
-	MultidimArray<double> allnodes_perframe_bfactors;
+	MultidimArray<DOUBLE> allnodes_perframe_bfactors;
 	allnodes_perframe_bfactors.resize(perframe_bfactors);
-	MPI_Allreduce(MULTIDIM_ARRAY(perframe_bfactors), MULTIDIM_ARRAY(allnodes_perframe_bfactors), MULTIDIM_SIZE(perframe_bfactors), MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
+	MPI_Allreduce(MULTIDIM_ARRAY(perframe_bfactors), MULTIDIM_ARRAY(allnodes_perframe_bfactors), MULTIDIM_SIZE(perframe_bfactors), MY_MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
 	perframe_bfactors = allnodes_perframe_bfactors;
 
 	if (verb > 0)
@@ -204,6 +203,7 @@ void ParticlePolisherMpi::calculateAllSingleFrameReconstructionsAndBfactors()
 	    writeStarFileRelativeWeights(fn_star);
 	}
 
+
 }
 
 void ParticlePolisherMpi::polishParticlesAllMicrographs()
@@ -255,8 +255,8 @@ void ParticlePolisherMpi::reconstructShinyParticlesAndFscWeight(int ipass)
 	if (verb > 0)
 		std::cout << "+ Reconstructing two halves of shiny particles ..." << std::endl;
 
-	// Re-read the shiny particles' metadatatable
-	exp_model.read(fn_out + ".star");
+	// Re-read the shiny particles' metadatatable (ignore original particle names here...)
+	exp_model.read(fn_out + ".star", true);
 
 	 // Do the reconstructions for both halves
 	if (node->rank == 0)
@@ -307,8 +307,8 @@ void ParticlePolisherMpi::reconstructShinyParticlesAndFscWeight(int ipass)
 	// Wait until the FSC-weighting has been done
 	MPI_Barrier(MPI_COMM_WORLD);
 
-	MultidimArray<double> dum;
-	Image<double> refvol;
+	MultidimArray<DOUBLE> dum;
+	Image<DOUBLE> refvol;
 	FileName fn_vol;
 	fn_vol = fn_in.withoutExtension() + "_" + fn_out + "_half1_class001_unfil.mrc";
 	refvol.read(fn_vol);
@@ -316,6 +316,7 @@ void ParticlePolisherMpi::reconstructShinyParticlesAndFscWeight(int ipass)
 	PPrefvol_half1.padding_factor = 2;
 	PPrefvol_half1.interpolator = TRILINEAR;
 	PPrefvol_half1.r_min_nn = 10;
+	PPrefvol_half1.data_dim = 2;
 	PPrefvol_half1.computeFourierTransformMap(refvol(), dum);
 	fn_vol = fn_in.withoutExtension() + "_" + fn_out + "_half2_class001_unfil.mrc";
 	refvol.read(fn_vol);
@@ -323,6 +324,7 @@ void ParticlePolisherMpi::reconstructShinyParticlesAndFscWeight(int ipass)
 	PPrefvol_half2.padding_factor = 2;
 	PPrefvol_half2.interpolator = TRILINEAR;
 	PPrefvol_half2.r_min_nn = 10;
+	PPrefvol_half2.data_dim = 2;
 	PPrefvol_half2.computeFourierTransformMap(refvol(), dum);
 
 }
@@ -376,17 +378,17 @@ void ParticlePolisherMpi::optimiseBeamTilt()
 	// Combine results from all nodes
 	if (beamtilt_max > 0.)
 	{
-		MultidimArray<double> allnodes_diff2_beamtilt;
+		MultidimArray<DOUBLE> allnodes_diff2_beamtilt;
 		allnodes_diff2_beamtilt.initZeros(diff2_beamtilt);
-		MPI_Allreduce(MULTIDIM_ARRAY(diff2_beamtilt), MULTIDIM_ARRAY(allnodes_diff2_beamtilt), MULTIDIM_SIZE(diff2_beamtilt), MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
+		MPI_Allreduce(MULTIDIM_ARRAY(diff2_beamtilt), MULTIDIM_ARRAY(allnodes_diff2_beamtilt), MULTIDIM_SIZE(diff2_beamtilt), MY_MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
 		diff2_beamtilt = allnodes_diff2_beamtilt;
 	}
 
 	if (defocus_shift_max > 0.)
 	{
-		MultidimArray<double> allnodes_defocus_shift_allmics;
+		MultidimArray<DOUBLE> allnodes_defocus_shift_allmics;
 		allnodes_defocus_shift_allmics.initZeros(defocus_shift_allmics);
-		MPI_Allreduce(MULTIDIM_ARRAY(defocus_shift_allmics), MULTIDIM_ARRAY(allnodes_defocus_shift_allmics), MULTIDIM_SIZE(defocus_shift_allmics), MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
+		MPI_Allreduce(MULTIDIM_ARRAY(defocus_shift_allmics), MULTIDIM_ARRAY(allnodes_defocus_shift_allmics), MULTIDIM_SIZE(defocus_shift_allmics), MY_MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
 		defocus_shift_allmics = allnodes_defocus_shift_allmics;
 	}
 
@@ -406,7 +408,7 @@ void ParticlePolisherMpi::run()
 		fitMovementsAllMicrographs();
 
 	// Perform single-frame reconstructions to estimate dose-dependent B-factors
-	if (do_weighting)
+        if (do_weighting)
             calculateAllSingleFrameReconstructionsAndBfactors();
 
 	// Write out the polished particles
diff --git a/src/particle_sorter.cpp b/src/particle_sorter.cpp
index ca46b82..6ed4917 100644
--- a/src/particle_sorter.cpp
+++ b/src/particle_sorter.cpp
@@ -81,7 +81,7 @@ void ParticleSorter::initialise()
 		FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDref)
 		{
 			// Get all reference images and their names
-			Image<double> Iref;
+			Image<DOUBLE> Iref;
 
 			FileName fn_img;
 			if (!MDref.getValue(EMDL_MLMODEL_REF_IMAGE, fn_img))
@@ -96,7 +96,7 @@ void ParticleSorter::initialise()
 	}
 	else
 	{
-		Image<double> Istk, Iref;
+		Image<DOUBLE> Istk, Iref;
 		Istk.read(fn_ref);
 		for (int n = 0; n < NSIZE(Istk()); n++)
 		{
@@ -135,7 +135,7 @@ void ParticleSorter::initialise()
 
 	// Calculate (downsized) Fourier transforms of the references
 	PPref.clear();
-	MultidimArray<double> dummy;
+	MultidimArray<DOUBLE> dummy;
 	Projector projector(particle_size);
 	for (int iref = 0; iref < Mrefs.size(); iref++)
 	{
@@ -189,8 +189,8 @@ void ParticleSorter::calculateFeaturesOneParticle(long int ipart)
 {
 
 	FourierTransformer transformer;
-	Image<double> img;
-	MultidimArray<double> Mref_rot;
+	Image<DOUBLE> img;
+	MultidimArray<DOUBLE> Mref_rot;
 	MultidimArray<Complex > Fimg, Fref;
 
 	// Get the image
@@ -205,13 +205,13 @@ void ParticleSorter::calculateFeaturesOneParticle(long int ipart)
 	}
 #ifdef DEBUG
 	std::cerr << " fn_img= " << fn_img << std::endl;
-	Image<double> tt;
+	Image<DOUBLE> tt;
 	tt = img;
 	tt.write("debug_ori_image.spi");
 #endif
 
 	// If the particle has non-zero shifts, then apply the rounded shifts here!
-	Matrix1D<double> offset(2);
+	Matrix1D<DOUBLE> offset(2);
 	MDin.getValue(EMDL_ORIENT_ORIGIN_X, XX(offset), ipart);
 	MDin.getValue(EMDL_ORIENT_ORIGIN_Y, YY(offset), ipart);
 	offset.selfROUND();
@@ -226,13 +226,13 @@ void ParticleSorter::calculateFeaturesOneParticle(long int ipart)
 	// Low-pass filter the image if necessary
 	if (lowpass > 0.)
 	{
-		double radius = XSIZE(img()) * angpix / lowpass;
+		DOUBLE radius = XSIZE(img()) * angpix / lowpass;
 		radius -= WIDTH_FMASK_EDGEB / 2.;
-		double radius_p = radius + WIDTH_FMASK_EDGEB;
+		DOUBLE radius_p = radius + WIDTH_FMASK_EDGEB;
 		transformer.FourierTransform(img(), Fimg);
 		FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(Fimg)
 		{
-			double r = sqrt((double)(kp*kp + ip*ip + jp*jp));
+			DOUBLE r = sqrt((DOUBLE)(kp*kp + ip*ip + jp*jp));
 			if (r < radius)
 				continue;
 			else if (r > radius_p)
@@ -251,9 +251,9 @@ void ParticleSorter::calculateFeaturesOneParticle(long int ipart)
 #endif
 
 	// Get the transformation parameters and which reference this is
-	double psi = 0.;
-	double rot = 0.;
-	double tilt = 0.;
+	DOUBLE psi = 0.;
+	DOUBLE rot = 0.;
+	DOUBLE tilt = 0.;
 	int iref;
 	MDin.getValue(EMDL_ORIENT_ROT, rot, ipart);
 	MDin.getValue(EMDL_ORIENT_TILT, tilt, ipart);
@@ -285,7 +285,7 @@ void ParticleSorter::calculateFeaturesOneParticle(long int ipart)
 	}
 
 	// Get the rotational matrix
-	Matrix2D<double> A;
+	Matrix2D<DOUBLE> A;
 	Euler_angles2matrix(rot, tilt, psi, A);
 
 	// Get the reference image in the right orientation
@@ -294,7 +294,7 @@ void ParticleSorter::calculateFeaturesOneParticle(long int ipart)
 	if (do_ctf)
 	{
 		CTF ctf;
-		MultidimArray<double> Fctf;
+		MultidimArray<DOUBLE> Fctf;
 		ctf.read(MDin, MDin, ipart);
 		Fctf.resize(Fref);
 		ctf.getFftwImage(Fctf, XSIZE(img()), YSIZE(img()), angpix, false, false, intact_ctf_first_peak, true);
@@ -321,8 +321,8 @@ void ParticleSorter::calculateFeaturesOneParticle(long int ipart)
 
 	/*
 	// Calculate the optimal scale between the image and the reference:
-	double sumxa = 0.;
-	double suma2 = 0.;
+	DOUBLE sumxa = 0.;
+	DOUBLE suma2 = 0.;
 	FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(Mref_rot)
 	{
 		if (DIRECT_MULTIDIM_ELEM(iMsk, n) > 0.5)
@@ -333,7 +333,7 @@ void ParticleSorter::calculateFeaturesOneParticle(long int ipart)
 	}
 	if (suma2 < 1e-10)
 		REPORT_ERROR("ERROR: Empty reference encountered!!");
-	double scale = sumxa/suma2;
+	DOUBLE scale = sumxa/suma2;
 	// Calculate the difference between the particle and the oriented reference
 	img() -= scale * Mref_rot;
 	*/
@@ -356,7 +356,7 @@ void ParticleSorter::calculateFeaturesOneParticle(long int ipart)
 
 	// Calculate real-space statistics on the difference image (only within the masked protein area)
 	//iMsk.initConstant(1.);
-    double mean = 0., stddev = 0., skew = 0., kurt = 0., quadrant_stddev = 0., rotsym = 0.;
+    DOUBLE mean = 0., stddev = 0., skew = 0., kurt = 0., quadrant_stddev = 0., rotsym = 0.;
 	calculateStatsOneImage(img(), mean, stddev, skew, kurt, quadrant_stddev);
 	DIRECT_A2D_ELEM(features, ipart, FEATURE_DF_AVG) = mean;
 	DIRECT_A2D_ELEM(features, ipart, FEATURE_DF_SIG) = stddev;
@@ -384,12 +384,12 @@ void ParticleSorter::normaliseFeatures()
 {
 
 	// Ignore manually added particles, which will have all features set to 0...
-	std::vector< std::vector<double> > normfeatures(XSIZE(features));
-	MultidimArray<double> onerow;
+	std::vector< std::vector<DOUBLE> > normfeatures(XSIZE(features));
+	MultidimArray<DOUBLE> onerow;
 	for (int ipart = 0; ipart < YSIZE(features); ipart++)
 	{
 		features.getRow(ipart, onerow);
-		double abssum = 0.;
+		DOUBLE abssum = 0.;
 		for (int ifeat = 0; ifeat < XSIZE(features); ifeat++)
 		{
 			abssum += ABS(DIRECT_A1D_ELEM(onerow, ifeat));
@@ -404,7 +404,7 @@ void ParticleSorter::normaliseFeatures()
 	}
 
 	// Now calculate average and stddev for all particles which did not have all features set to 0
-	std::vector<double> avg(XSIZE(features), 0.), stddev(XSIZE(features), 0.);
+	std::vector<DOUBLE> avg(XSIZE(features), 0.), stddev(XSIZE(features), 0.);
 	for (int ifeat = 0; ifeat < XSIZE(features); ifeat++)
 	{
 		// average
@@ -422,7 +422,7 @@ void ParticleSorter::normaliseFeatures()
 	for (int ipart = 0; ipart < YSIZE(features); ipart++)
 	{
 		features.getRow(ipart, onerow);
-		double abssum = 0.;
+		DOUBLE abssum = 0.;
 		for (int ifeat = 0; ifeat < XSIZE(features); ifeat++)
 		{
 			abssum += ABS(DIRECT_A1D_ELEM(onerow, ifeat));
@@ -465,7 +465,7 @@ void ParticleSorter::writeFeatures()
 		REPORT_ERROR( (std::string)" ParticleSorter::writeFeatures: Cannot write file: " + fn_tmp);
 #endif
 
-	MultidimArray<double> sumfeatures(YSIZE(features));
+	MultidimArray<DOUBLE> sumfeatures(YSIZE(features));
 	sumfeatures.initZeros();
 	for (int ipart = 0; ipart < YSIZE(features); ipart++)
 	{
@@ -482,7 +482,7 @@ void ParticleSorter::writeFeatures()
 		}
 		DIRECT_A1D_ELEM(sumfeatures, ipart) /= XSIZE(features);
 #ifdef WRITE_LIBSVM
-		MultidimArray<double> row(1);
+		MultidimArray<DOUBLE> row(1);
 		features.getRow(ipart, row);
 		fh2<< " sum= " << DIRECT_A1D_ELEM(sumfeatures, ipart);
 		MDin.getValue(EMDL_IMAGE_NAME, fn_img, ipart);
@@ -518,13 +518,13 @@ void ParticleSorter::writeFeatures()
 		std::cout <<" Written out "<< fn_tmp << std::endl;
 }
 
-double ParticleSorter::rotationalSymmetryFourierTransform(MultidimArray<Complex > &Fimg)
+DOUBLE ParticleSorter::rotationalSymmetryFourierTransform(MultidimArray<Complex > &Fimg)
 {
-	double result;
+	DOUBLE result;
 	// Calculate average and stddev per ring
 
 	int radius = ROUND(YSIZE(Fimg) * angpix / lowpass);
-	MultidimArray<double> count, power;
+	MultidimArray<DOUBLE> count, power;
 
     power.initZeros(radius+1);
     count.initZeros(radius+1);
@@ -543,15 +543,15 @@ double ParticleSorter::rotationalSymmetryFourierTransform(MultidimArray<Complex
             power(i) /= count(i);
     }
 
-    double sum = 0.;
-    double sum2 = 0.;
-    double cc  = 0.;
+    DOUBLE sum = 0.;
+    DOUBLE sum2 = 0.;
+    DOUBLE cc  = 0.;
     FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM2D(Fimg)
     {
     	long int idx = ROUND(sqrt(ip*ip + jp*jp));
     	if (idx < radius)
     	{
-    		double aux = norm(DIRECT_A2D_ELEM(Fimg, i, j)) - power(idx);
+    		DOUBLE aux = norm(DIRECT_A2D_ELEM(Fimg, i, j)) - power(idx);
     		sum += aux;
     		sum2 += aux*aux;
     		cc++;
@@ -568,8 +568,8 @@ double ParticleSorter::rotationalSymmetryFourierTransform(MultidimArray<Complex
 	return result;
 }
 
-void ParticleSorter::calculateStatsOneImage(MultidimArray<double> &img,
-							double &mean, double &stddev, double &skew, double &kurt, double &quadrant_stddev)
+void ParticleSorter::calculateStatsOneImage(MultidimArray<DOUBLE> &img,
+							DOUBLE &mean, DOUBLE &stddev, DOUBLE &skew, DOUBLE &kurt, DOUBLE &quadrant_stddev)
 {
 
 	// First pass: calculate mean
@@ -578,14 +578,14 @@ void ParticleSorter::calculateStatsOneImage(MultidimArray<double> &img,
 	int n2 = 0;
 	int n3 = 0;
 	int n4 = 0;
-	double mean_q1 = 0.;
-	double mean_q2 = 0.;
-	double mean_q3 = 0.;
-	double mean_q4 = 0.;
-	double stddev_q1 = 0.;
-	double stddev_q2 = 0.;
-	double stddev_q3 = 0.;
-	double stddev_q4 = 0.;
+	DOUBLE mean_q1 = 0.;
+	DOUBLE mean_q2 = 0.;
+	DOUBLE mean_q3 = 0.;
+	DOUBLE mean_q4 = 0.;
+	DOUBLE stddev_q1 = 0.;
+	DOUBLE stddev_q2 = 0.;
+	DOUBLE stddev_q3 = 0.;
+	DOUBLE stddev_q4 = 0.;
 	mean = 0.;
 	stddev = 0;
 	skew   = 0.;
@@ -634,8 +634,8 @@ void ParticleSorter::calculateStatsOneImage(MultidimArray<double> &img,
 	{
 		if (i*i + j*j < particle_radius2)
 		{
-			double aux = A2D_ELEM(img, i, j) - mean;
-			double aux2 = aux * aux;
+			DOUBLE aux = A2D_ELEM(img, i, j) - mean;
+			DOUBLE aux2 = aux * aux;
 			stddev += aux2;
 			skew += aux2 * aux;
 			kurt += aux2 * aux2;
@@ -665,7 +665,7 @@ void ParticleSorter::calculateStatsOneImage(MultidimArray<double> &img,
 			}
 		}
 	}
-	double var = stddev/na;
+	DOUBLE var = stddev/na;
 	stddev = sqrt(var);
 	skew = (skew/na)/(var*stddev);
 	kurt = (kurt/na)/(var*var) - 3.;
@@ -682,8 +682,8 @@ void ParticleSorter::calculateStatsOneImage(MultidimArray<double> &img,
 	var = stddev_q4/n4;
 	stddev_q4 = sqrt(var);
 
-	double mean_stddevs = 0.;
-	double stddev_stddevs = 0.;
+	DOUBLE mean_stddevs = 0.;
+	DOUBLE stddev_stddevs = 0.;
 	mean_stddevs = (stddev_q1 + stddev_q2 + stddev_q3 + stddev_q4) / 4.;
 	stddev_stddevs += (stddev_q1-mean_stddevs)*(stddev_q1-mean_stddevs) + (stddev_q2-mean_stddevs)*(stddev_q2-mean_stddevs) + (stddev_q3-mean_stddevs)*(stddev_q3-mean_stddevs) + (stddev_q4-mean_stddevs)*(stddev_q4-mean_stddevs);
 	quadrant_stddev = sqrt(stddev_stddevs / 4.);
diff --git a/src/particle_sorter.h b/src/particle_sorter.h
index 8691fdd..4f00aa8 100644
--- a/src/particle_sorter.h
+++ b/src/particle_sorter.h
@@ -57,14 +57,14 @@ public:
 	MetaDataTable MDin;
 
 	// Pixel size (for low-pass filter and particle diameter)
-	double angpix;
+	DOUBLE angpix;
 
 	// Particle diameter (in Angstroms)
-	double particle_diameter;
+	DOUBLE particle_diameter;
 	int particle_radius2;
 
 	// Low pass filetr cutoff (in Angstroms)
-	double lowpass;
+	DOUBLE lowpass;
 
 	// Original size of the reference images
 	int particle_size;
@@ -73,16 +73,16 @@ public:
 	int current_size;
 
 	// Minimum Z-value to count in the sorting
-	double min_z;
+	DOUBLE min_z;
 
 	// Vector with all original reference images
-	std::vector<MultidimArray<double> > Mrefs;
+	std::vector<MultidimArray<DOUBLE> > Mrefs;
 
 	// FTs of the reference images for feature calculation
 	std::vector<Projector > PPref;
 
 	// Feature values for all input images
-	MultidimArray<double> features;
+	MultidimArray<DOUBLE> features;
 
 	// Is density in micrograph inverted wrt templates?
 	bool do_invert;
@@ -115,10 +115,10 @@ protected:
 	// Write out (for now in libsvm format)
 	void writeFeatures();
 
-	void calculateStatsOneImage(MultidimArray<double> &img,
-			double &mean, double &stddev, double &skew, double &kurt, double &quadrant_stddev);
+	void calculateStatsOneImage(MultidimArray<DOUBLE> &img,
+			DOUBLE &mean, DOUBLE &stddev, DOUBLE &skew, DOUBLE &kurt, DOUBLE &quadrant_stddev);
 
-	double rotationalSymmetryFourierTransform(MultidimArray<Complex > &Fimg);
+	DOUBLE rotationalSymmetryFourierTransform(MultidimArray<Complex > &Fimg);
 
 };
 
diff --git a/src/particle_sorter_mpi.cpp b/src/particle_sorter_mpi.cpp
index 1fe04c6..c2569b9 100644
--- a/src/particle_sorter_mpi.cpp
+++ b/src/particle_sorter_mpi.cpp
@@ -75,9 +75,9 @@ void ParticleSorterMpi::run()
 		progress_bar(my_nr_images);
 
 	// Combine results from all nodes
-	MultidimArray<double> allnodes_features;
+	MultidimArray<DOUBLE> allnodes_features;
 	allnodes_features.resize(features);
-	MPI_Allreduce(MULTIDIM_ARRAY(features), MULTIDIM_ARRAY(allnodes_features), MULTIDIM_SIZE(features), MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
+	MPI_Allreduce(MULTIDIM_ARRAY(features), MULTIDIM_ARRAY(allnodes_features), MULTIDIM_SIZE(features), MY_MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
 	features = allnodes_features;
 
 	// Only the master writes out files
diff --git a/src/postprocessing.cpp b/src/postprocessing.cpp
index 440db85..c28d79c 100644
--- a/src/postprocessing.cpp
+++ b/src/postprocessing.cpp
@@ -162,7 +162,7 @@ bool Postprocessing::getMask()
 		Im().setXmippOrigin();
 
 		// Check values are between 0 and 1
-		double avg, stddev, minval, maxval;
+		DOUBLE avg, stddev, minval, maxval;
 		Im().computeStats(avg, stddev, minval, maxval);
 		if (minval < -1e-6 || maxval - 1. > 1.e-6)
 		{
@@ -202,9 +202,66 @@ void Postprocessing::divideByMtf(MultidimArray<Complex > &FT)
 			std::cout.width(35); std::cout << std::left <<"  + mtf STAR-file: "; std::cout << fn_mtf << std::endl;
 		}
 
-		correctMapForMTF(FT, XSIZE(I1()), fn_mtf);
+		MetaDataTable MDmtf;
+
+		if (!fn_mtf.isStarFile())
+			REPORT_ERROR("Postprocessing::divideByMtf ERROR: input MTF file is not a STAR file.");
+
+		MDmtf.read(fn_mtf);
+		MultidimArray<DOUBLE> mtf_resol, mtf_value;
+		mtf_resol.resize(MDmtf.numberOfObjects());
+		mtf_value.resize(mtf_resol);
+
+		int i =0;
+		FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDmtf)
+		{
+			MDmtf.getValue(EMDL_RESOLUTION_INVPIXEL, DIRECT_A1D_ELEM(mtf_resol, i) ); // resolution needs to be given in 1/pix
+			MDmtf.getValue(EMDL_POSTPROCESS_MTF_VALUE, DIRECT_A1D_ELEM(mtf_value, i) );
+			if (DIRECT_A1D_ELEM(mtf_value, i) < 1e-10)
+			{
+				std::cerr << " i= " << i <<  " mtf_value[i]= " << DIRECT_A1D_ELEM(mtf_value, i) << std::endl;
+				REPORT_ERROR("Postprocessing::sharpenMap ERROR: zero or negative values encountered in MTF curve!");
+			}
+			i++;
+		}
+
+	    DOUBLE xsize = (DOUBLE)XSIZE(I1());
+	    FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(FT)
+	    {
+	    	int r2 = kp * kp + ip * ip + jp * jp;
+	    	DOUBLE res = sqrt((DOUBLE)r2)/xsize; // get resolution in 1/pixel
+			if (res < 0.5 )
+			{
+
+				// Find the suitable MTF value
+				int i_0 = 0;
+				for (int ii = 0; ii < XSIZE(mtf_resol); ii++)
+				{
+					if (DIRECT_A1D_ELEM(mtf_resol, ii) > res)
+						break;
+					i_0 = ii;
+				}
+				// linear interpolation: y = y_0 + (y_1 - y_0)*(x-x_0)/(x1_x0)
+				DOUBLE mtf;
+				DOUBLE x_0 = DIRECT_A1D_ELEM(mtf_resol, i_0);
+				if (i_0 == MULTIDIM_SIZE(mtf_resol) - 1 || i_0 == 0) // check boundaries of the array
+					mtf = DIRECT_A1D_ELEM(mtf_value, i_0);
+				else
+				{
+					DOUBLE x_1 = DIRECT_A1D_ELEM(mtf_resol, i_0 + 1);
+					DOUBLE y_0 = DIRECT_A1D_ELEM(mtf_value, i_0);
+					DOUBLE y_1 = DIRECT_A1D_ELEM(mtf_value, i_0 + 1);
+					mtf = y_0 + (y_1 - y_0)*(res - x_0)/(x_1 - x_0);
+				}
+
+				// Divide Fourier component by the MTF
+				DIRECT_A3D_ELEM(FT, k, i, j) /= mtf;
+			}
+	    }
+
 	}
 
+
 }
 
 
@@ -278,7 +335,7 @@ void Postprocessing::sharpenMap()
 	{
 		std::cout << "== Low-pass filtering final map ... " << std::endl;
 	}
-	double my_filter = (low_pass_freq > 0.) ? low_pass_freq : global_resol;
+	DOUBLE my_filter = (low_pass_freq > 0.) ? low_pass_freq : global_resol;
 	if (verb > 0)
 	{
 		std::cout.width(35); std::cout << std::left  <<"  + filter frequency: "; std::cout << my_filter << std::endl;
@@ -289,7 +346,7 @@ void Postprocessing::sharpenMap()
 
 }
 
-void Postprocessing::applyFscWeighting(MultidimArray<Complex > &FT, MultidimArray<double> my_fsc)
+void Postprocessing::applyFscWeighting(MultidimArray<Complex > &FT, MultidimArray<DOUBLE> my_fsc)
 {
 	// Find resolution where fsc_true drops below zero for the first time
 	// Set all weights to zero beyond that resolution
@@ -302,10 +359,10 @@ void Postprocessing::applyFscWeighting(MultidimArray<Complex > &FT, MultidimArra
 	}
 	FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(FT)
 	{
-    	int ires = ROUND(sqrt((double)kp * kp + ip * ip + jp * jp));
+    	int ires = ROUND(sqrt((DOUBLE)kp * kp + ip * ip + jp * jp));
 		if (ires <= ires_max)
 		{
-	        double fsc = DIRECT_A1D_ELEM(my_fsc, ires);
+	        DOUBLE fsc = DIRECT_A1D_ELEM(my_fsc, ires);
 	        if (fsc < 1e-10)
 	        	REPORT_ERROR("Postprocessing::applyFscWeighting BUG: fsc <= 0");
 	        DIRECT_A3D_ELEM(FT, k, i, j) *= sqrt((2 * fsc) / (1 + fsc));
@@ -322,13 +379,13 @@ void Postprocessing::makeGuinierPlot(MultidimArray<Complex > &FT, std::vector<fi
 {
 
 	MultidimArray<int> radial_count(XSIZE(FT));
-	MultidimArray<double> lnF(XSIZE(FT));
+	MultidimArray<DOUBLE> lnF(XSIZE(FT));
 	fit_point2D      onepoint;
 
 	FOR_ALL_ELEMENTS_IN_FFTW_TRANSFORM(FT)
 	{
     	int r2 = kp * kp + ip * ip + jp * jp;
-    	int ires = ROUND(sqrt((double)r2));
+    	int ires = ROUND(sqrt((DOUBLE)r2));
 		if (ires < XSIZE(radial_count))
 		{
 
@@ -337,12 +394,12 @@ void Postprocessing::makeGuinierPlot(MultidimArray<Complex > &FT, std::vector<fi
 		}
 	}
 
-	double xsize = XSIZE(I1());
+	DOUBLE xsize = XSIZE(I1());
 	guinier.clear();
 	FOR_ALL_ELEMENTS_IN_ARRAY1D(radial_count)
 	{
 
-		double res = (xsize * angpix)/(double)i; // resolution in Angstrom
+		DOUBLE res = (xsize * angpix)/(DOUBLE)i; // resolution in Angstrom
 		if (res >= angpix * 2.) // Apply B-factor sharpening until Nyquist, then low-pass filter later on (with a soft edge)
         {
             onepoint.x = 1. / (res * res);
@@ -381,7 +438,7 @@ void Postprocessing::writeOutput()
 	FileName fn_tmp;
 	fn_tmp = fn_out + ".mrc";
 
-	double avg, stddev, minval, maxval;
+	DOUBLE avg, stddev, minval, maxval;
     I1().computeStats(avg, stddev, minval, maxval);
     I1.MDMainHeader.setValue(EMDL_IMAGE_STATS_MIN, minval);
     I1.MDMainHeader.setValue(EMDL_IMAGE_STATS_MAX, maxval);
@@ -465,7 +522,7 @@ void Postprocessing::writeOutput()
 	FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY1D(fsc_true)
 	{
 		MDfsc.addObject();
-		double res = (i > 0) ? (XSIZE(I1()) * angpix / (double)i) : 999.;
+		DOUBLE res = (i > 0) ? (XSIZE(I1()) * angpix / (DOUBLE)i) : 999.;
 		MDfsc.setValue(EMDL_SPECTRAL_IDX, (int)i);
 		MDfsc.setValue(EMDL_RESOLUTION, 1./res);
 		MDfsc.setValue(EMDL_RESOLUTION_ANGSTROM, res);
@@ -525,7 +582,7 @@ void Postprocessing::writeFscXml(MetaDataTable &MDfsc)
 
     FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDfsc)
     {
-    	double xx, yy;
+    	DOUBLE xx, yy;
     	MDfsc.getValue(EMDL_RESOLUTION, xx);
     	MDfsc.getValue(EMDL_POSTPROCESS_FSC_TRUE, yy);
     	fh << "  <coordinate>" << std::endl;
@@ -535,7 +592,7 @@ void Postprocessing::writeFscXml(MetaDataTable &MDfsc)
     }
     fh << "</fsc>" << std::endl;
     fh.close();
-    std::cout.width(35); std::cout << std::left <<"  + EMDB xml-format FSC curve: "; std::cout << fn_fsc << std::endl;
+
 }
 
 void Postprocessing::run()
@@ -591,21 +648,22 @@ void Postprocessing::run()
 
 		}
 		else
-			REPORT_ERROR("Postprocessing::run ERROR: FSC curve never drops below randomize_fsc_at");
+			REPORT_ERROR("Postprocessing::run ERROR: FSC curve never drops below randomize_fsc_at.  You may want to check your mask.");
 
 		// Now that we have fsc_masked and fsc_random_masked, calculate fsc_true according to Richard's formula
 		// FSC_true = FSC_t - FSC_n / ( )
 		fsc_true.resize(fsc_masked);
 		FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY1D(fsc_true)
 		{
-			if (i < randomize_at)
+                    // 29jan2015: let's move this 2 shells upwards, because of small artefacts near the resolution of randomisation!
+			if (i < randomize_at + 2)
 			{
 				DIRECT_A1D_ELEM(fsc_true, i) = DIRECT_A1D_ELEM(fsc_masked, i);
 			}
 			else
 			{
-				double fsct = DIRECT_A1D_ELEM(fsc_masked, i);
-				double fscn = DIRECT_A1D_ELEM(fsc_random_masked, i);
+				DOUBLE fsct = DIRECT_A1D_ELEM(fsc_masked, i);
+				DOUBLE fscn = DIRECT_A1D_ELEM(fsc_random_masked, i);
 				if (fscn > fsct)
 					DIRECT_A1D_ELEM(fsc_true, i) = 0.;
 				else
@@ -631,7 +689,7 @@ void Postprocessing::run()
 	{
 		if ( DIRECT_A1D_ELEM(fsc_true, i) < 0.143)
 			break;
-		global_resol = XSIZE(I1())*angpix/(double)i;
+		global_resol = XSIZE(I1())*angpix/(DOUBLE)i;
 	}
 
 	// Add the two half-maps together for subsequent sharpening
diff --git a/src/postprocessing.h b/src/postprocessing.h
index aed30c1..0c10254 100644
--- a/src/postprocessing.h
+++ b/src/postprocessing.h
@@ -45,10 +45,10 @@ public:
 	FileName fn_in, fn_out, fn_I1, fn_I2;
 
 	// Images for the two half-reconstructions and the mask
-	Image<double> I1, I2, I1phi, I2phi, Im;
+	Image<DOUBLE> I1, I2, I1phi, I2phi, Im;
 
 	// Pixel size in Angstroms
-	double angpix;
+	DOUBLE angpix;
 
 	/////// Masking
 
@@ -56,16 +56,16 @@ public:
 	bool do_auto_mask;
 
 	// Density threshold below which to calculate initial mask seed
-	double ini_mask_density_threshold;
+	DOUBLE ini_mask_density_threshold;
 
 	// Number of pixels to extend the mask beyond the initial mask seed
-	double extend_ini_mask;
+	DOUBLE extend_ini_mask;
 
 	// Width (in pixels) for soft mask edge
-	double width_soft_mask_edge;
+	DOUBLE width_soft_mask_edge;
 
 	// From the resolution where the FSC drops below this value, randomize the phases in the two maps
-	double randomize_fsc_at;
+	DOUBLE randomize_fsc_at;
 
 	// Filename for a user-provided mask
 	FileName fn_mask;
@@ -82,10 +82,10 @@ public:
 	bool do_mask;
 
 	// Minimum and maximum resolution to use in the fit
-	double fit_minres, fit_maxres;
+	DOUBLE fit_minres, fit_maxres;
 
 	// User-provided (ad hoc) B-factor
-	double adhoc_bfac;
+	DOUBLE adhoc_bfac;
 
 	///////// Filtering
 
@@ -93,15 +93,15 @@ public:
 	bool do_fsc_weighting;
 
 	// Frequency at which to low-pass filter the final map
-	double low_pass_freq;
+	DOUBLE low_pass_freq;
 
 	// Width of raised cosine edge on low-pass filter
 	int filter_edge_width;
 
 	// Arrays to store FSC, Guinier curves etc
-	MultidimArray<double> fsc_unmasked;
-	MultidimArray<double> fsc_masked, fsc_random_masked, fsc_true;
-	double global_intercept, global_slope, global_corr_coeff, global_bfactor, global_resol;
+	MultidimArray<DOUBLE> fsc_unmasked;
+	MultidimArray<DOUBLE> fsc_masked, fsc_random_masked, fsc_true;
+	DOUBLE global_intercept, global_slope, global_corr_coeff, global_bfactor, global_resol;
 	// The Guinier plots
 	std::vector<fit_point2D>  guinierin, guinierinvmtf, guinierweighted, guiniersharpen;
 
@@ -135,7 +135,7 @@ public:
 	void makeGuinierPlot(MultidimArray<Complex > &FT, std::vector<fit_point2D> &guinier);
 
 	// Apply sqrt(2FSC/(FSC=1)) weighting prior to B-factor sharpening
-	void applyFscWeighting(MultidimArray<Complex > &FT, MultidimArray<double> my_fsc);
+	void applyFscWeighting(MultidimArray<Complex > &FT, MultidimArray<DOUBLE> my_fsc);
 
 	// Output map and STAR files with metadata, also write final resolution to screen
 	void writeOutput();
diff --git a/src/preprocessing.cpp b/src/preprocessing.cpp
index a171b38..67a9971 100644
--- a/src/preprocessing.cpp
+++ b/src/preprocessing.cpp
@@ -51,6 +51,7 @@ void Preprocessing::read(int argc, char **argv, int rank)
 	scale  = textToInteger(parser.getOption("--scale", "Re-scale the particles to this size (in pixels)", "-1"));
 	window  = textToInteger(parser.getOption("--window", "Re-window the particles to this size (in pixels)", "-1"));
 	do_normalise = parser.checkOption("--norm", "Normalise the background to average zero and stddev one");
+	do_ramp = !parser.checkOption("--no_ramp", "Just subtract the background mean in the normalisation, instead of subtracting a fitted ramping background. ");
 	bg_radius = textToInteger(parser.getOption("--bg_radius", "Radius of the circular mask that will be used to define the background area (in pixels)", "-1"));
 	white_dust_stddev = textToFloat(parser.getOption("--white_dust", "Sigma-values above which white dust will be removed (negative value means no dust removal)","-1"));
 	black_dust_stddev = textToFloat(parser.getOption("--black_dust", "Sigma-values above which black dust will be removed (negative value means no dust removal)","-1"));
@@ -176,7 +177,7 @@ void Preprocessing::joinAllStarFiles()
 
 	std::cout << " Joining all metadata in one STAR file..." << std::endl;
 	bool has_other_ctfs, has_this_ctf;
-	double defU, defV, defAng, CC, HT, CS, AmpCnst, XMAG, DStep;
+	DOUBLE defU, defV, defAng, CC, HT, CS, AmpCnst, XMAG, DStep;
 	has_other_ctfs = false;
 	FileName prev_fn_mic="";
 	for (long int ipos = 0; ipos < fn_coords.size(); ipos++)
@@ -190,7 +191,7 @@ void Preprocessing::joinAllStarFiles()
 		{
 			FileName fn_microot = getRootNameFromMicrographName(fn_mic);
 			// Gather the results from ctffind
-			has_this_ctf = getCtffind3Results(fn_microot, defU, defV, defAng, CC,
+			has_this_ctf = getCtffindResults(fn_microot, defU, defV, defAng, CC,
 					HT, CS, AmpCnst, XMAG, DStep);
 
 		}
@@ -198,7 +199,7 @@ void Preprocessing::joinAllStarFiles()
 
 			// Re-scaled detector pixel size
 		if (has_this_ctf && do_rescale)
-                    DStep *= (double)extract_size/(double)scale;
+                    DStep *= (DOUBLE)extract_size/(DOUBLE)scale;
 
 		if (ipos == 0 && has_this_ctf)
 		{
@@ -217,10 +218,10 @@ void Preprocessing::joinAllStarFiles()
 		}
 
 		if (!has_this_ctf && has_other_ctfs)
-			REPORT_ERROR("joinAllStarFiles%ERROR: Exiting because of missing CTFFIND3 logfiles for micrograph " + fn_mic);
+			REPORT_ERROR("joinAllStarFiles%ERROR: Exiting because of missing CTFFIND logfiles for micrograph " + fn_mic);
 
 		if (has_this_ctf && !has_other_ctfs)
-			REPORT_ERROR("joinAllStarFiles%ERROR: Exiting because of missing CTFFIND3 logfiles ...");
+			REPORT_ERROR("joinAllStarFiles%ERROR: Exiting because of missing CTFFIND logfiles ...");
 
 
 		FileName fn_star = "Particles/" + fn_mic.withoutExtension() + "_" + fn_out + ".star";
@@ -228,7 +229,7 @@ void Preprocessing::joinAllStarFiles()
 
 		FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDonestack)
 		{
-			// This was double, right?
+			// This was DOUBLE, right?
 			//MDonestack.setValue(EMDL_MICROGRAPH_NAME, fn_mic);
 
 			if (has_this_ctf)
@@ -349,8 +350,8 @@ void Preprocessing::readCoordinates(FileName fn_coord, MetaDataTable &MD)
 				int ypos = num2 + num4 / 2;
 
 				MD.addObject();
-				MD.setValue(EMDL_IMAGE_COORD_X, (double)xpos);
-				MD.setValue(EMDL_IMAGE_COORD_Y, (double)ypos);
+				MD.setValue(EMDL_IMAGE_COORD_X, (DOUBLE)xpos);
+				MD.setValue(EMDL_IMAGE_COORD_Y, (DOUBLE)ypos);
 			}
 			else // Try reading as plain ASCII....
 			{
@@ -366,12 +367,12 @@ void Preprocessing::readCoordinates(FileName fn_coord, MetaDataTable &MD)
 					if (words.size() > 1 && sscanf(words[0].c_str(), "%d", &num1) && sscanf(words[1].c_str(), "%d", &num2))
 					{
 						MD.addObject();
-						MD.setValue(EMDL_IMAGE_COORD_X, (double)num1);
-						MD.setValue(EMDL_IMAGE_COORD_Y, (double)num2);
+						MD.setValue(EMDL_IMAGE_COORD_X, (DOUBLE)num1);
+						MD.setValue(EMDL_IMAGE_COORD_Y, (DOUBLE)num2);
 
 						// It could also be a X,Y,Z coordinate...
 						if (words.size() > 2 && sscanf(words[2].c_str(), "%d", &num3))
-							MD.setValue(EMDL_IMAGE_COORD_Z, (double)num3);
+							MD.setValue(EMDL_IMAGE_COORD_Z, (DOUBLE)num3);
 					}
 
 
@@ -388,11 +389,11 @@ void Preprocessing::readCoordinates(FileName fn_coord, MetaDataTable &MD)
 					int num3;
 
 					MD.addObject();
-					MD.setValue(EMDL_IMAGE_COORD_X, (double)num1);
-					MD.setValue(EMDL_IMAGE_COORD_Y, (double)num2);
+					MD.setValue(EMDL_IMAGE_COORD_X, (DOUBLE)num1);
+					MD.setValue(EMDL_IMAGE_COORD_Y, (DOUBLE)num2);
 					// It could also be a X,Y,Z coordinate...
 					if (words.size() > 2 && sscanf(words[2].c_str(), "%d", &num3))
-						MD.setValue(EMDL_IMAGE_COORD_Z, (double)num3);
+						MD.setValue(EMDL_IMAGE_COORD_Z, (DOUBLE)num3);
 
 				}
 			}
@@ -415,7 +416,7 @@ void Preprocessing::extractParticlesFromFieldOfView(FileName fn_coord)
 	{
 		FOR_ALL_OBJECTS_IN_METADATA_TABLE(MDin)
 		{
-			double xcoor, ycoor;
+			DOUBLE xcoor, ycoor;
 			MDin.getValue(EMDL_IMAGE_COORD_X, xcoor);
 			MDin.getValue(EMDL_IMAGE_COORD_Y, ycoor);
 			xcoor += extract_bias_x;
@@ -443,13 +444,15 @@ void Preprocessing::extractParticlesFromFieldOfView(FileName fn_coord)
 	}
 
 	// Read the header of the micrograph to see how many frames there are.
-	Image<double> Imic;
+	Image<DOUBLE> Imic;
 	Imic.read(fn_mic, false, -1, false); // readData = false, select_image = -1, mapData= false, is_2D = true);
 
 	int xdim, ydim, zdim;
 	long int ndim;
 	Imic.getDimensions(xdim, ydim, zdim, ndim);
 	dimensionality = (zdim > 1) ? 3 : 2;
+	if (dimensionality == 3 || do_movie_extract )
+            do_ramp = false;
 
 	// Name of the output stack
 	// Add the same root as the output STAR file (that way one could extract two "families" of different particle stacks)
@@ -462,10 +465,10 @@ void Preprocessing::extractParticlesFromFieldOfView(FileName fn_coord)
 		std::cout << "WARNING: movie " << fn_mic << " does not have multiple frames..." << std::endl;
 
 	long int my_current_nr_images = 0;
-	double all_avg = 0;
-	double all_stddev = 0;
-	double all_minval = 99.e99;
-	double all_maxval = -99.e99;
+	DOUBLE all_avg = 0;
+	DOUBLE all_stddev = 0;
+	DOUBLE all_minval = 99.e99;
+	DOUBLE all_maxval = -99.e99;
 
 	// To deal with default movie_last_frame value
 	if (movie_last_frame < 0)
@@ -496,10 +499,10 @@ void Preprocessing::extractParticlesFromFieldOfView(FileName fn_coord)
 void Preprocessing::extractParticlesFromOneFrame(MetaDataTable &MD,
 		FileName fn_mic, int iframe, int n_frames,
 		FileName fn_output_img_root, long int &my_current_nr_images, long int my_total_nr_images,
-		double &all_avg, double &all_stddev, double &all_minval, double &all_maxval)
+		DOUBLE &all_avg, DOUBLE &all_stddev, DOUBLE &all_minval, DOUBLE &all_maxval)
 {
 
-	Image<double> Ipart, Imic, Itmp;
+	Image<DOUBLE> Ipart, Imic, Itmp;
 
 	FileName fn_frame;
 	// If movies, then average over avg_n_frames
@@ -509,8 +512,8 @@ void Preprocessing::extractParticlesFromOneFrame(MetaDataTable &MD,
 		{
 			int iiframe = iframe + ii;
 			// If we run over the size of the movie, then discard these frames
-			if (iiframe >= n_frames)
-				return;
+			if (iiframe >= movie_first_frame + n_frames)
+				break;
 			fn_frame.compose(iiframe + 1, fn_mic);
 			if (ii==0)
 			{
@@ -534,7 +537,7 @@ void Preprocessing::extractParticlesFromOneFrame(MetaDataTable &MD,
 	int ipos = 0;
 	FOR_ALL_OBJECTS_IN_METADATA_TABLE(MD)
 	{
-		double dxpos, dypos, dzpos;
+		DOUBLE dxpos, dypos, dzpos;
 		long int xpos, ypos, zpos;
 		long int x0, xF, y0, yF, z0, zF;
 		MD.getValue(EMDL_IMAGE_COORD_X, dxpos);
@@ -617,7 +620,7 @@ void Preprocessing::extractParticlesFromOneFrame(MetaDataTable &MD,
 			if (dimensionality == 3 && do_project_3d)
 			{
 				// Project the 3D sub-tomogram into a 2D particle again
-				Image<double> Iproj(YSIZE(Ipart()), XSIZE(Ipart()));
+				Image<DOUBLE> Iproj(YSIZE(Ipart()), XSIZE(Ipart()));
 				Iproj().setXmippOrigin();
 				FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY3D(Ipart())
 				{
@@ -643,7 +646,7 @@ void Preprocessing::extractParticlesFromOneFrame(MetaDataTable &MD,
 				fn_part.compose(ipos + 1,  "Particles/" + getRootNameFromMicrographName(fn_mic)); // start image counting in stacks at 1!
 				// for automated re-alignment of particles in relion_refine: have rlnParticleName equal to rlnImageName in non-movie star file
 				fn_part += "_" + fn_out + ".mrcs";
-				MD.setValue(EMDL_PARTICLE_NAME, fn_part);
+				MD.setValue(EMDL_PARTICLE_ORI_NAME, fn_part);
 			}
 			MD.setValue(EMDL_IMAGE_NAME, fn_img);
 			MD.setValue(EMDL_MICROGRAPH_NAME, fn_frame);
@@ -657,7 +660,7 @@ void Preprocessing::extractParticlesFromOneFrame(MetaDataTable &MD,
 
 void Preprocessing::runOperateOnInputFile(FileName fn_operate_on)
 {
-	Image<double> Ipart, Iout;
+	Image<DOUBLE> Ipart, Iout;
 	MetaDataTable MD;
 	long int Nimg;
 
@@ -682,10 +685,10 @@ void Preprocessing::runOperateOnInputFile(FileName fn_operate_on)
 		Nimg = NSIZE(Iout());
 	}
 
-	double all_avg = 0;
-	double all_stddev = 0;
-	double all_minval = 99.e99;
-	double all_maxval = -99.e99;
+	DOUBLE all_avg = 0;
+	DOUBLE all_stddev = 0;
+	DOUBLE all_minval = 99.e99;
+	DOUBLE all_maxval = -99.e99;
 	init_progress_bar(Nimg);
 	int barstep = XMIPP_MAX(1, Nimg / 120);
 	for (long int i = 0; i < Nimg; i++)
@@ -730,8 +733,8 @@ void Preprocessing::runOperateOnInputFile(FileName fn_operate_on)
 }
 
 
-void Preprocessing::performPerImageOperations(Image<double> &Ipart, FileName fn_output_img_root, int nframes, long int image_nr, long int nr_of_images,
-		double &all_avg, double &all_stddev, double &all_minval, double &all_maxval)
+void Preprocessing::performPerImageOperations(Image<DOUBLE> &Ipart, FileName fn_output_img_root, int nframes, long int image_nr, long int nr_of_images,
+		DOUBLE &all_avg, DOUBLE &all_stddev, DOUBLE &all_minval, DOUBLE &all_maxval)
 {
 
 	Ipart().setXmippOrigin();
@@ -740,16 +743,16 @@ void Preprocessing::performPerImageOperations(Image<double> &Ipart, FileName fn_
 
 	if (do_rewindow) rewindow(Ipart, window);
 
-	if (do_normalise) normalise(Ipart, bg_radius, white_dust_stddev, black_dust_stddev);
+	if (do_normalise) normalise(Ipart, bg_radius, white_dust_stddev, black_dust_stddev, do_ramp);
 
 	if (do_invert_contrast) invert_contrast(Ipart);
 
 	// For movies: multiple the image intensities by sqrt(nframes) so the stddev in the average of the normalised frames is again 1
 	if (nframes > 1)
-		Ipart() *= sqrt((double)nframes/(double)avg_n_frames);
+		Ipart() *= sqrt((DOUBLE)nframes/(DOUBLE)avg_n_frames);
 
 	// Calculate mean, stddev, min and max
-	double avg, stddev, minval, maxval;
+	DOUBLE avg, stddev, minval, maxval;
 	Ipart().computeStats(avg, stddev, minval, maxval);
 
 	if (Ipart().getDim() == 3)
diff --git a/src/preprocessing.h b/src/preprocessing.h
index d345ad3..30c7f3a 100644
--- a/src/preprocessing.h
+++ b/src/preprocessing.h
@@ -84,7 +84,7 @@ public:
 	int extract_size;
 
 	// Bias in picked coordinates in X and in Y direction (in pixels)
-	double extract_bias_x, extract_bias_y;
+	DOUBLE extract_bias_x, extract_bias_y;
 
 	////////////////////////////////////// Post-extraction image modifications
 	// Perform re-scaling of extracted images
@@ -98,11 +98,14 @@ public:
 	// Perform normalization of the extract images
 	bool do_normalise;
 
+	// Subtract ramp instead of a level background in normalization
+	bool do_ramp;
+
 	// Perform contrast inversion of the extracted images
 	bool do_invert_contrast;
 
 	// Standard deviations to remove black and white dust
-	double white_dust_stddev, black_dust_stddev;
+	DOUBLE white_dust_stddev, black_dust_stddev;
 
 	// Radius of a circle in the extracted images outside of which one calculates background mean and stddev
 	int bg_radius;
@@ -145,14 +148,14 @@ public:
 	// Actually extract particles. This can be from one (average) micrgraph or from a single frame from a movie
 	void extractParticlesFromOneFrame(MetaDataTable &MD,
 			FileName fn_mic, int iframe, int n_frames, FileName fn_output_img_root, long int &my_current_nr_images, long int my_total_nr_images,
-			double &all_avg, double &all_stddev, double &all_minval, double &all_maxval);
+			DOUBLE &all_avg, DOUBLE &all_stddev, DOUBLE &all_minval, DOUBLE &all_maxval);
 
 	// Perform per-image operations (e.g. normalise, rescaling, rewindowing and inverting contrast) on an input stack (or STAR file)
 	void runOperateOnInputFile(FileName fn_perimage_in);
 
 	// Here normalisation, windowing etc is performed on an individual image and it is written to disc
-	void performPerImageOperations(Image<double> &Ipart, FileName fn_output_img_root, int nframes, long int image_nr, long int nr_of_images,
-			double &all_avg, double &all_stddev, double &all_minval, double &all_maxval);
+	void performPerImageOperations(Image<DOUBLE> &Ipart, FileName fn_output_img_root, int nframes, long int image_nr, long int nr_of_images,
+			DOUBLE &all_avg, DOUBLE &all_stddev, DOUBLE &all_minval, DOUBLE &all_maxval);
 
 	// Get micrograph name from the rootname
 	// The rootname may have an additional string after the uniqye micrograph name
diff --git a/src/projector.cpp b/src/projector.cpp
index e4aa431..71e8ab7 100644
--- a/src/projector.cpp
+++ b/src/projector.cpp
@@ -77,16 +77,16 @@ long int Projector::getSize()
 }
 
 // Fill data array with oversampled Fourier transform, and calculate its power spectrum
-void Projector::computeFourierTransformMap(MultidimArray<double> &vol_in, MultidimArray<double> &power_spectrum, int current_size, int nr_threads, bool do_gridding)
+void Projector::computeFourierTransformMap(MultidimArray<DOUBLE> &vol_in, MultidimArray<DOUBLE> &power_spectrum, int current_size, int nr_threads, bool do_gridding)
 {
 
-	MultidimArray<double> Mpad;
+	MultidimArray<DOUBLE> Mpad;
 	MultidimArray<Complex > Faux;
     FourierTransformer transformer;
     // DEBUGGING: multi-threaded FFTWs are giving me a headache?
 	// For a long while: switch them off!
 	//transformer.setThreadsNumber(nr_threads);
-    double normfft;
+    DOUBLE normfft;
 
 	// Size of padded real-space volume
 	int padoridim = padding_factor * ori_size;
@@ -99,28 +99,24 @@ void Projector::computeFourierTransformMap(MultidimArray<double> &vol_in, Multid
 	{
 	case 2:
 	   Mpad.initZeros(padoridim, padoridim);
-	   normfft = (double)(padding_factor * padding_factor);
+	   normfft = (DOUBLE)(padding_factor * padding_factor);
 	   break;
 	case 3:
 	   Mpad.initZeros(padoridim, padoridim, padoridim);
-	   normfft = (double)(padding_factor * padding_factor * padding_factor * ori_size);
+	   if (data_dim ==3)
+		   normfft = (DOUBLE)(padding_factor * padding_factor * padding_factor);
+	   else
+		   normfft = (DOUBLE)(padding_factor * padding_factor * padding_factor * ori_size);
 	   break;
 	default:
-	   REPORT_ERROR("Projector::get2DSlice%%ERROR: Dimension of the data array should be 2 or 3");
+	   REPORT_ERROR("Projector::computeFourierTransformMap%%ERROR: Dimension of the data array should be 2 or 3");
 	}
 
 	// First do a gridding pre-correction on the real-space map:
 	// Divide by the inverse Fourier transform of the interpolator in Fourier-space
 	// 10feb11: at least in 2D case, this seems to be the wrong thing to do!!!
-	// Still to check the 3D case...
-	/////////////////// TODO: removed this for subtomo averaging!!!! Go back and check 3d-> 2d projection!!!!!
-	/////////////////// TODO: removed this for subtomo averaging!!!! Go back and check 3d-> 2d projection!!!!!
-	/////////////////// TODO: removed this for subtomo averaging!!!! Go back and check 3d-> 2d projection!!!!!
-	/////////////////// TODO: removed this for subtomo averaging!!!! Go back and check 3d-> 2d projection!!!!!
-	/////////////////// TODO: removed this for subtomo averaging!!!! Go back and check 3d-> 2d projection!!!!!
-	/////////////////// TODO: removed this for subtomo averaging!!!! Go back and check 3d-> 2d projection!!!!!
-	/////////////////// TODO: removed this for subtomo averaging!!!! Go back and check 3d-> 2d projection!!!!!
-	if (do_gridding)
+	// TODO: check what is best for subtomo!
+	if (do_gridding)// && data_dim != 3)
 		griddingCorrect(vol_in);
 
 	// Pad translated map with zeros
@@ -145,7 +141,7 @@ void Projector::computeFourierTransformMap(MultidimArray<double> &vol_in, Multid
 	// (other points will be zero because of initZeros() call above
 	// Also calculate radial power spectrum
 	power_spectrum.initZeros(ori_size / 2 + 1);
-	MultidimArray<double> counter(power_spectrum);
+	MultidimArray<DOUBLE> counter(power_spectrum);
 	counter.initZeros();
 
 	int max_r2 = r_max * r_max * padding_factor * padding_factor;
@@ -159,7 +155,7 @@ void Projector::computeFourierTransformMap(MultidimArray<double> &vol_in, Multid
 			A3D_ELEM(data, kp, ip, jp) = DIRECT_A3D_ELEM(Faux, k, i, j) * normfft;
 
 			// Calculate power spectrum
-			int ires = ROUND( sqrt((double)r2) / padding_factor );
+			int ires = ROUND( sqrt((DOUBLE)r2) / padding_factor );
 			// Factor two because of two-dimensionality of the complex plane
 			DIRECT_A1D_ELEM(power_spectrum, ires) += norm(A3D_ELEM(data, kp, ip, jp)) / 2.;
 			DIRECT_A1D_ELEM(counter, ires) += 1.;
@@ -179,19 +175,19 @@ void Projector::computeFourierTransformMap(MultidimArray<double> &vol_in, Multid
 
 }
 
-void Projector::griddingCorrect(MultidimArray<double> &vol_in)
+void Projector::griddingCorrect(MultidimArray<DOUBLE> &vol_in)
 {
 	// Correct real-space map by dividing it by the Fourier transform of the interpolator(s)
 	vol_in.setXmippOrigin();
 	FOR_ALL_ELEMENTS_IN_ARRAY3D(vol_in)
 	{
-		double r = sqrt((double)(k*k+i*i+j*j));
+		DOUBLE r = sqrt((DOUBLE)(k*k+i*i+j*j));
 		// if r==0: do nothing (i.e. divide by 1)
 		if (r > 0.)
 		{
-			double rval = r / (ori_size * padding_factor);
-			double sinc = sin(PI * rval) / ( PI * rval);
-			//double ftblob = blob_Fourier_val(rval, blob) / blob_Fourier_val(0., blob);
+			DOUBLE rval = r / (ori_size * padding_factor);
+			DOUBLE sinc = sin(PI * rval) / ( PI * rval);
+			//DOUBLE ftblob = blob_Fourier_val(rval, blob) / blob_Fourier_val(0., blob);
 			// Interpolation (goes with "interpolator") to go from arbitrary to fine grid
 			if (interpolator==NEAREST_NEIGHBOUR && r_min_nn == 0)
 			{
@@ -214,14 +210,14 @@ void Projector::griddingCorrect(MultidimArray<double> &vol_in)
 	}
 }
 
-void Projector::project(MultidimArray<Complex > &f2d, Matrix2D<double> &A, bool inv)
+void Projector::project(MultidimArray<Complex > &f2d, Matrix2D<DOUBLE> &A, bool inv)
 {
-	double fx, fy, fz, xp, yp, zp;
+	DOUBLE fx, fy, fz, xp, yp, zp;
 	int x0, x1, y0, y1, z0, z1, y, y2, r2;
 	bool is_neg_x;
 	Complex d000, d001, d010, d011, d100, d101, d110, d111;
 	Complex dx00, dx01, dx10, dx11, dxy0, dxy1;
-	Matrix2D<double> Ainv;
+	Matrix2D<DOUBLE> Ainv;
 
     // f2d should already be in the right size (ori_size,orihalfdim)
     // AND the points outside r_max should already be zero...
@@ -237,7 +233,7 @@ void Projector::project(MultidimArray<Complex > &f2d, Matrix2D<double> &A, bool
     int my_r_max = XMIPP_MIN(r_max, XSIZE(f2d) - 1);
 
     // Go from the 2D slice coordinates to the 3D coordinates
-    Ainv *= (double)padding_factor;  // take scaling into account directly
+    Ainv *= (DOUBLE)padding_factor;  // take scaling into account directly
     int max_r2 = my_r_max * my_r_max;
     int min_r2_nn = r_min_nn * r_min_nn;
 
@@ -362,13 +358,13 @@ void Projector::project(MultidimArray<Complex > &f2d, Matrix2D<double> &A, bool
 #endif
 }
 
-void Projector::rotate2D(MultidimArray<Complex > &f2d, Matrix2D<double> &A, bool inv)
+void Projector::rotate2D(MultidimArray<Complex > &f2d, Matrix2D<DOUBLE> &A, bool inv)
 {
-	double fx, fy, xp, yp;
+	DOUBLE fx, fy, xp, yp;
 	int x0, x1, y0, y1, y, y2, r2;
 	bool is_neg_x;
 	Complex d00, d01, d10, d11, dx0, dx1;
-	Matrix2D<double> Ainv;
+	Matrix2D<DOUBLE> Ainv;
 
     // f2d should already be in the right size (ori_size,orihalfdim)
     // AND the points outside max_r should already be zero...
@@ -383,7 +379,7 @@ void Projector::rotate2D(MultidimArray<Complex > &f2d, Matrix2D<double> &A, bool
     int my_r_max = XMIPP_MIN(r_max, XSIZE(f2d) - 1);
 
     // Go from the 2D slice coordinates to the map coordinates
-    Ainv *= (double)padding_factor;  // take scaling into account directly
+    Ainv *= (DOUBLE)padding_factor;  // take scaling into account directly
     int max_r2 = my_r_max * my_r_max;
     int min_r2_nn = r_min_nn * r_min_nn;
 #ifdef DEBUG
@@ -476,3 +472,167 @@ void Projector::rotate2D(MultidimArray<Complex > &f2d, Matrix2D<double> &A, bool
 		} // endif x-loop
 	} // endif y-loop
 }
+
+
+void Projector::rotate3D(MultidimArray<Complex > &f3d, Matrix2D<DOUBLE> &A, bool inv)
+{
+	DOUBLE fx, fy, fz, xp, yp, zp;
+	int x0, x1, y0, y1, z0, z1, y, z, y2, z2, r2;
+	bool is_neg_x;
+	Complex d000, d010, d100, d110, d001, d011, d101, d111, dx00, dx10, dxy0, dx01, dx11, dxy1;
+	Matrix2D<DOUBLE> Ainv;
+
+    // f3d should already be in the right size (ori_size,orihalfdim)
+    // AND the points outside max_r should already be zero...
+    // f3d.initZeros();
+	// Use the inverse matrix
+    if (inv)
+    	Ainv = A;
+    else
+    	Ainv = A.transpose();
+
+    // The f3d image may be smaller than r_max, in that case also make sure not to fill the corners!
+    int my_r_max = XMIPP_MIN(r_max, XSIZE(f3d) - 1);
+
+    // Go from the 3D rotated coordinates to the original map coordinates
+    Ainv *= (DOUBLE)padding_factor;  // take scaling into account directly
+    int max_r2 = my_r_max * my_r_max;
+    int min_r2_nn = r_min_nn * r_min_nn;
+#ifdef DEBUG
+    std::cerr << " XSIZE(f3d)= "<< XSIZE(f3d) << std::endl;
+    std::cerr << " YSIZE(f3d)= "<< YSIZE(f3d) << std::endl;
+    std::cerr << " XSIZE(data)= "<< XSIZE(data) << std::endl;
+    std::cerr << " YSIZE(data)= "<< YSIZE(data) << std::endl;
+    std::cerr << " STARTINGX(data)= "<< STARTINGX(data) << std::endl;
+    std::cerr << " STARTINGY(data)= "<< STARTINGY(data) << std::endl;
+    std::cerr << " STARTINGZ(data)= "<< STARTINGZ(data) << std::endl;
+    std::cerr << " max_r= "<< r_max << std::endl;
+    std::cerr << " Ainv= " << Ainv << std::endl;
+#endif
+	for (int k=0; k < ZSIZE(f3d); k++)
+	{
+		// Don't search beyond square with side max_r
+		if (k <= my_r_max)
+		{
+			z = k;
+		}
+		else if (k >= ZSIZE(f3d) - my_r_max)
+		{
+			z = k - ZSIZE(f3d);
+		}
+		else
+			continue;
+		z2 = z * z;
+
+		for (int i=0; i < YSIZE(f3d); i++)
+		{
+			// Don't search beyond square with side max_r
+			if (i <= my_r_max)
+			{
+				y = i;
+			}
+			else if (i >= YSIZE(f3d) - my_r_max)
+			{
+				y = i - YSIZE(f3d);
+			}
+			else
+				continue;
+			y2 = y * y;
+
+			for (int x=0; x <= my_r_max; x++)
+			{
+				// Only include points with radius < max_r (exclude points outside circle in square)
+				r2 = x * x + y2 + z2;
+				if (r2 > max_r2)
+					continue;
+
+				// Get logical coordinates in the 3D map
+				xp = Ainv(0,0) * x + Ainv(0,1) * y + Ainv(0,2) * z;
+				yp = Ainv(1,0) * x + Ainv(1,1) * y + Ainv(1,2) * z;
+				zp = Ainv(2,0) * x + Ainv(2,1) * y + Ainv(2,2) * z;
+
+				if (interpolator == TRILINEAR || r2 < min_r2_nn)
+				{
+					// Only asymmetric half is stored
+					if (xp < 0)
+					{
+						// Get complex conjugated hermitian symmetry pair
+						xp = -xp;
+						yp = -yp;
+						zp = -zp;
+						is_neg_x = true;
+					}
+					else
+					{
+						is_neg_x = false;
+					}
+
+					// Trilinear interpolation (with physical coords)
+					// Subtract STARTINGY to accelerate access to data (STARTINGX=0)
+					// In that way use DIRECT_A3D_ELEM, rather than A3D_ELEM
+					x0 = FLOOR(xp);
+					fx = xp - x0;
+					x1 = x0 + 1;
+
+					y0 = FLOOR(yp);
+					fy = yp - y0;
+					y0 -=  STARTINGY(data);
+					y1 = y0 + 1;
+
+					z0 = FLOOR(zp);
+					fz = zp - z0;
+					z0 -=  STARTINGZ(data);
+					z1 = z0 + 1;
+
+					// Matrix access can be accelerated through pre-calculation of z0*xydim etc.
+					d000 = DIRECT_A3D_ELEM(data, z0, y0, x0);
+					d001 = DIRECT_A3D_ELEM(data, z0, y0, x1);
+					d010 = DIRECT_A3D_ELEM(data, z0, y1, x0);
+					d011 = DIRECT_A3D_ELEM(data, z0, y1, x1);
+					d100 = DIRECT_A3D_ELEM(data, z1, y0, x0);
+					d101 = DIRECT_A3D_ELEM(data, z1, y0, x1);
+					d110 = DIRECT_A3D_ELEM(data, z1, y1, x0);
+					d111 = DIRECT_A3D_ELEM(data, z1, y1, x1);
+
+					// Set the interpolated value in the 2D output array
+					// interpolate in x
+					dx00 = LIN_INTERP(fx, d000, d001);
+					dx01 = LIN_INTERP(fx, d100, d101);
+					dx10 = LIN_INTERP(fx, d010, d011);
+					dx11 = LIN_INTERP(fx, d110, d111);
+					// interpolate in y
+					dxy0 = LIN_INTERP(fy, dx00, dx10);
+					dxy1 = LIN_INTERP(fy, dx01, dx11);
+					//interpolate in z
+					DIRECT_A3D_ELEM(f3d, k, i, x) = LIN_INTERP(fz, dxy0, dxy1);
+
+					// Take complex conjugated for half with negative x
+					if (is_neg_x)
+						DIRECT_A3D_ELEM(f3d, k, i, x) = conj(DIRECT_A3D_ELEM(f3d, k, i, x));
+
+				} // endif TRILINEAR
+				else if (interpolator == NEAREST_NEIGHBOUR )
+				{
+					x0 = ROUND(xp);
+					y0 = ROUND(yp);
+					z0 = ROUND(zp);
+
+					if (x0 < 0)
+						DIRECT_A3D_ELEM(f3d, k, i, x) = conj(A3D_ELEM(data, -z0, -y0, -x0));
+					else
+						DIRECT_A3D_ELEM(f3d, k, i, x) = A3D_ELEM(data, z0, y0, x0);
+
+				} // endif NEAREST_NEIGHBOUR
+				else
+					REPORT_ERROR("Unrecognized interpolator in Projector::project");
+			} // endif x-loop
+		} // endif y-loop
+	} // endif z-loop
+}
+
+
+
+
+
+
+
diff --git a/src/projector.h b/src/projector.h
index 6ee2e51..d1b158c 100644
--- a/src/projector.h
+++ b/src/projector.h
@@ -61,6 +61,9 @@ public:
     // Dimension of the reference (currently allowed 2 or 3)
     int ref_dim;
 
+    // Dimension of the projections (2 or 3)
+    int data_dim;
+
 public:
 
     /** Empty constructor
@@ -84,7 +87,7 @@ public:
      * Projector PPref(ori_size, NEAREST_NEIGHBOUR);
      * @endcode
      */
-    Projector(int _ori_size, int _interpolator = TRILINEAR, int _padding_factor_3d = 2, int _r_min_nn = 10)
+    Projector(int _ori_size, int _interpolator = TRILINEAR, int _padding_factor_3d = 2, int _r_min_nn = 10, int _data_dim = 2)
     {
 
     	// Store original dimension
@@ -99,6 +102,8 @@ public:
     	// Minimum radius for NN interpolation
     	r_min_nn = _r_min_nn;
 
+    	// Dimension of the projections
+    	data_dim = _data_dim;
     }
 
     /** Copy constructor
@@ -133,6 +138,7 @@ public:
         	interpolator = op.interpolator;
         	padding_factor = op.padding_factor;
         	ref_dim = op.ref_dim;
+        	data_dim  = op.data_dim;
         }
         return *this;
     }
@@ -156,7 +162,7 @@ public:
     void clear()
     {
     	data.clear();
-    	r_max = r_min_nn = interpolator = padding_factor = ref_dim = pad_size = 0;
+    	r_max = r_min_nn = interpolator = padding_factor = ref_dim = data_dim = pad_size = 0;
     }
 
     /*
@@ -183,43 +189,57 @@ public:
     * Depending on whether 2D or 3D Fourier Transforms will be extracted, the map is normalized internally in a different manner
     *
     */
-   void computeFourierTransformMap(MultidimArray<double> &vol_in, MultidimArray<double> &power_spectrum, int current_size = -1, int nr_threads = 1, bool do_gridding = true);
+   void computeFourierTransformMap(MultidimArray<DOUBLE> &vol_in, MultidimArray<DOUBLE> &power_spectrum, int current_size = -1, int nr_threads = 1, bool do_gridding = true);
 
    /* Because we interpolate in Fourier space to make projections and/or reconstructions, we have to correct
     * the real-space maps by dividing them by the Fourier Transform of the interpolator
     * Note these corrections are made on the not-oversampled, i.e. originally sized real-space map
     */
-   void griddingCorrect(MultidimArray<double> &vol_in);
+   void griddingCorrect(MultidimArray<DOUBLE> &vol_in);
 
    /*
 	* Get a 2D Fourier Transform from the 2D or 3D data array
 	* Depending on the dimension of the map, this will be a projection or a rotation operation
 	*/
-	void get2DFourierTransform(MultidimArray<Complex > &img_out, Matrix2D<double> &A, bool inv)
+	void get2DFourierTransform(MultidimArray<Complex > &img_out, Matrix2D<DOUBLE> &A, bool inv)
 	{
 		// Rotation of a 3D Fourier Transform
-		switch (ref_dim)
+		if (data_dim == 3)
+		{
+			if (ref_dim != 3)
+				REPORT_ERROR("Projector::get3DFourierTransform%%ERROR: Dimension of the data array should be 3");
+			rotate3D(img_out, A, inv);
+		}
+		else
 		{
-		case 2:
-		   rotate2D(img_out, A, inv);
-		   break;
-		case 3:
-		   project(img_out, A, inv);
-		   break;
-		default:
-		   REPORT_ERROR("Projector::get2DSlice%%ERROR: Dimension of the data array should be 2 or 3");
+			switch (ref_dim)
+			{
+			case 2:
+			   rotate2D(img_out, A, inv);
+			   break;
+			case 3:
+			   project(img_out, A, inv);
+			   break;
+			default:
+			   REPORT_ERROR("Projector::get2DSlice%%ERROR: Dimension of the data array should be 2 or 3");
+			}
 		}
 	}
 
 	/*
 	* Get a 2D slice from the 3D map (forward projection)
 	*/
-	void project(MultidimArray<Complex > &img_out, Matrix2D<double> &A, bool inv);
+	void project(MultidimArray<Complex > &img_out, Matrix2D<DOUBLE> &A, bool inv);
 
 	/*
 	* Get an in-plane rotated version of the 2D map (mere interpolation)
 	*/
-	void rotate2D(MultidimArray<Complex > &img_out, Matrix2D<double> &A, bool inv);
+	void rotate2D(MultidimArray<Complex > &img_out, Matrix2D<DOUBLE> &A, bool inv);
+
+	/*
+	* Get a rotated version of the 3D map (mere interpolation)
+	*/
+	void rotate3D(MultidimArray<Complex > &img_out, Matrix2D<DOUBLE> &A, bool inv);
 
 
 };
diff --git a/src/rwIMAGIC.h b/src/rwIMAGIC.h
index bc35933..83e1d59 100644
--- a/src/rwIMAGIC.h
+++ b/src/rwIMAGIC.h
@@ -63,8 +63,8 @@ struct IMAGIChead
     float oldavd;      // 20      old average
     float densmax;       // 21      maximum
     float densmin;       // 22      minimum
-    //     double sum;       // 23+24  sum of densities
-    //     double squares;    // 25+26  sum of squares
+    //     DOUBLE sum;       // 23+24  sum of densities
+    //     DOUBLE squares;    // 25+26  sum of squares
     float dummy[4];   // 23-26  dummy place holder
     char lastpr[8];      // 27+28     last program writing file
     char name[80];       // 29-48     image name
@@ -174,13 +174,13 @@ int  readIMAGIC(long int img_select)
         header->densmax = header->avdens + header->sigma;
     }
 
-    MDMainHeader.setValue(EMDL_IMAGE_STATS_MIN,(double)header->densmin);
-    MDMainHeader.setValue(EMDL_IMAGE_STATS_MAX,(double)header->densmax);
-    MDMainHeader.setValue(EMDL_IMAGE_STATS_AVG,(double)header->avdens);
-    MDMainHeader.setValue(EMDL_IMAGE_STATS_STDDEV,(double)header->sigma);
-    MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_X,(double)1.);
-    MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Y,(double)1.);
-    MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Z,(double)1.);
+    MDMainHeader.setValue(EMDL_IMAGE_STATS_MIN,(DOUBLE)header->densmin);
+    MDMainHeader.setValue(EMDL_IMAGE_STATS_MAX,(DOUBLE)header->densmax);
+    MDMainHeader.setValue(EMDL_IMAGE_STATS_AVG,(DOUBLE)header->avdens);
+    MDMainHeader.setValue(EMDL_IMAGE_STATS_STDDEV,(DOUBLE)header->sigma);
+    MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_X,(DOUBLE)1.);
+    MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Y,(DOUBLE)1.);
+    MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Z,(DOUBLE)1.);
     MDMainHeader.setValue(EMDL_IMAGE_DATATYPE,(int)datatype);
 
     offset = 0;   // separate header file
@@ -263,7 +263,7 @@ int  writeIMAGIC(long int img_select=-1, int mode=WRITE_OVERWRITE)
         imgStart=0;
 
     // Convert T to datatype
-    if ( typeid(T) == typeid(double) ||
+    if ( typeid(T) == typeid(DOUBLE) ||
          typeid(T) == typeid(float) ||
          typeid(T) == typeid(int) )
         strcpy(header->type,"REAL");
@@ -276,7 +276,7 @@ int  writeIMAGIC(long int img_select=-1, int mode=WRITE_OVERWRITE)
     size_t datasize, datasize_n;
     datasize_n = Xdim*Ydim*Zdim;
     datasize = datasize_n * gettypesize(Float);
-    double aux;
+    DOUBLE aux;
 
     if (!MDMainHeader.isEmpty())
     {
diff --git a/src/rwMRC.h b/src/rwMRC.h
index 8897597..aae3672 100644
--- a/src/rwMRC.h
+++ b/src/rwMRC.h
@@ -28,6 +28,10 @@
 #ifndef RWMRC_H
 #define RWMRC_H
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif 
+
 #define MRCSIZE    1024 // Minimum size of the MRC header (when nsymbt = 0)
 
 ///@defgroup MRC MRC File format
@@ -233,18 +237,18 @@ int readMRC(long int img_select, bool isStack=false)
     }
     offset = MRCSIZE + header->nsymbt;
 
-    MDMainHeader.setValue(EMDL_IMAGE_STATS_MIN,(double)header->amin);
-    MDMainHeader.setValue(EMDL_IMAGE_STATS_MAX,(double)header->amax);
-    MDMainHeader.setValue(EMDL_IMAGE_STATS_AVG,(double)header->amean);
-    MDMainHeader.setValue(EMDL_IMAGE_STATS_STDDEV,(double)header->arms);
+    MDMainHeader.setValue(EMDL_IMAGE_STATS_MIN,(DOUBLE)header->amin);
+    MDMainHeader.setValue(EMDL_IMAGE_STATS_MAX,(DOUBLE)header->amax);
+    MDMainHeader.setValue(EMDL_IMAGE_STATS_AVG,(DOUBLE)header->amean);
+    MDMainHeader.setValue(EMDL_IMAGE_STATS_STDDEV,(DOUBLE)header->arms);
     MDMainHeader.setValue(EMDL_IMAGE_DATATYPE,(int)datatype);
 
     if ( header->mx && header->a!=0)//ux
-        MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_X,(double)header->a/header->mx);
+        MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_X,(DOUBLE)header->a/header->mx);
     if ( header->my && header->b!=0)//yx
-        MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Y,(double)header->b/header->my);
+        MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Y,(DOUBLE)header->b/header->my);
     if ( header->mz && header->c!=0)//zx
-        MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Z,(double)header->c/header->mz);
+        MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Z,(DOUBLE)header->c/header->mz);
 
    if (isStack && dataflag<0)   // Don't read the individual header and the data if not necessary
    {
@@ -320,7 +324,7 @@ int writeMRC(long int img_select, bool isStack=false, int mode=WRITE_OVERWRITE)
     	header->nz = Zdim;
 
     // Convert T to datatype
-    if ( typeid(T) == typeid(double) ||
+    if ( typeid(T) == typeid(DOUBLE) ||
          typeid(T) == typeid(float) ||
          typeid(T) == typeid(int) )
         header->mode = 2;
@@ -337,7 +341,7 @@ int writeMRC(long int img_select, bool isStack=false, int mode=WRITE_OVERWRITE)
     header->mapc = 1;
     header->mapr = 2;
     header->maps = 3;
-    double aux,aux2;
+    DOUBLE aux,aux2;
 
     // TODO: fix this!
     header->a = (float)0.;// ua;
@@ -399,7 +403,25 @@ int writeMRC(long int img_select, bool isStack=false, int mode=WRITE_OVERWRITE)
     }
 
     header->nsymbt = 0;
-    header->nlabl = 10; // or zero?
+
+    //Create label "Relion version    date time"
+#define MRC_LABEL_LEN 80
+    header->nlabl =  1;
+
+    char label[MRC_LABEL_LEN] = "Relion ";
+    time_t rawtime; 
+    struct tm * timeinfo;
+
+    time (&rawtime);
+    timeinfo = localtime (&rawtime);
+
+#ifdef PACKAGE_VERSION
+    strcat(label,PACKAGE_VERSION);
+#endif
+    strcat(label,"   ");
+    strftime (label+strlen(label),MRC_LABEL_LEN-strlen(label),"%d-%b-%y  %R:%S",timeinfo);
+    strncpy(header->labels,label,MRC_LABEL_LEN);
+
     //strncpy(header->labels, p->label.c_str(), 799);
 
     offset = MRCSIZE + header->nsymbt;
diff --git a/src/rwSPIDER.h b/src/rwSPIDER.h
index 07db268..ef0b929 100644
--- a/src/rwSPIDER.h
+++ b/src/rwSPIDER.h
@@ -89,14 +89,10 @@ struct SPIDERhead
     float Tend;
     float Tinc; // 4*3 = 12, 12+28 = 40B
 
-    /** Sjors Scheres 17/12/04 **/
-    float weight; // For Maximum-Likelihood refinement
-    float flip;   // 0=no flipping operation (false), 1=flipping (true)
-
     char fNada2[576]; // empty 700-76-40=624-40-8= 576 bytes
 
     char cdat[12];   // 211-213   creation date
-    char ctim[8];  // 214-215   creation time
+    char ctim[9];  // 214-215   creation time
     char ctit[160];  // 216-255   title
 } ;
 
@@ -157,13 +153,13 @@ int  readSPIDER(long int img_select)
     offset = (int) header->labbyt;
     DataType datatype  = Float;
 
-    MDMainHeader.setValue(EMDL_IMAGE_STATS_MIN,(double)header->fmin);
-    MDMainHeader.setValue(EMDL_IMAGE_STATS_MAX,(double)header->fmax);
-    MDMainHeader.setValue(EMDL_IMAGE_STATS_AVG,(double)header->av);
-    MDMainHeader.setValue(EMDL_IMAGE_STATS_STDDEV,(double)header->sig);
-    MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_X,(double)header->scale);
-    MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Y,(double)header->scale);
-    MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Z,(double)header->scale);
+    MDMainHeader.setValue(EMDL_IMAGE_STATS_MIN,(DOUBLE)header->fmin);
+    MDMainHeader.setValue(EMDL_IMAGE_STATS_MAX,(DOUBLE)header->fmax);
+    MDMainHeader.setValue(EMDL_IMAGE_STATS_AVG,(DOUBLE)header->av);
+    MDMainHeader.setValue(EMDL_IMAGE_STATS_STDDEV,(DOUBLE)header->sig);
+    MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_X,(DOUBLE)header->scale);
+    MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Y,(DOUBLE)header->scale);
+    MDMainHeader.setValue(EMDL_IMAGE_SAMPLINGRATE_Z,(DOUBLE)header->scale);
     MDMainHeader.setValue(EMDL_IMAGE_DATATYPE,(int)datatype);
 
     bool isStack = ( header->istack > 0 );
@@ -311,7 +307,7 @@ int  writeSPIDER(long int select_img=-1, bool isStack=false, int mode=WRITE_OVER
     	header->iform = 1;     // 2D image
     else
     	header->iform = 3;     // 3D volume
-    double aux;
+    DOUBLE aux;
     bool baux;
     header->imami = 0;//never trust max/min
 
diff --git a/src/strings.cpp b/src/strings.cpp
index 281b527..d28ac3f 100644
--- a/src/strings.cpp
+++ b/src/strings.cpp
@@ -18,7 +18,7 @@
  * author citations must be preserved.
  ***************************************************************************/
 /***************************************************************************
- * 
+ *
  * Authors:     J.R. Bilbao-Castro (jrbcast at ace.ual.es)
  *
  * Unidad de  Bioinformatica of Centro Nacional de Biotecnologia , CSIC
@@ -142,15 +142,19 @@ void trim(std::string& str)
 
 /* NOTE: not a very safe implemenation but standard c functions do not retrieve
  * more than 6 significative digits */
-double textToDouble(const char* str, int _errno, std::string errmsg)
+DOUBLE textToDOUBLE(const char* str, int _errno, std::string errmsg)
 {
-    double retval;
+    DOUBLE retval;
     int ok;
 
     if (str == NULL)
     	REPORT_ERROR( errmsg);
 
+#ifdef FLOAT_PRECISION
+    ok = sscanf(str, "%f", &retval);
+#else
     ok = sscanf(str, "%lf", &retval);
+#endif
 
     if (ok)
         return retval;
diff --git a/src/strings.h b/src/strings.h
index 171cd19..2ee8b4a 100644
--- a/src/strings.h
+++ b/src/strings.h
@@ -52,6 +52,7 @@
 #include <sstream>
 #include <string.h>
 #include <stdio.h>
+#include "src/macros.h"
 
 /// @defgroup StringUtilities String utilities
 /// @ingroup DataLibrary
@@ -105,13 +106,13 @@ std::string unescape( const std::string& str );
  */
 int bestPrecision(float F, int _width);
 
-/** String (char*) to double conversion.
+/** String (char*) to DOUBLE conversion.
  *
  * @code
- * double key = textToDouble(firstToken(line), 1602, "Error reading key");
+ * DOUBLE key = textToDOUBLE(firstToken(line), 1602, "Error reading key");
  * @endcode
  */
-double textToDouble(const char* str,
+DOUBLE textToDOUBLE(const char* str,
                     int _errno = 2101,
                     std::string errmsg = "Error in textToDouble");
 
@@ -256,7 +257,7 @@ std::string removeSpaces(const std::string& _str);
 
 /** Remove quotes.
  *
- * This function removes the first character if it is a double or single quote,
+ * This function removes the first character if it is a DOUBLE or single quote,
  * as well as the last character. The char pointer might be moved.
  *
  * @code
diff --git a/src/symmetries.cpp b/src/symmetries.cpp
index 6c2d443..f2e0c83 100644
--- a/src/symmetries.cpp
+++ b/src/symmetries.cpp
@@ -54,10 +54,10 @@ int SymList::read_sym_file(FileName fn_sym)
     FILE *fpoii;
     char line[80];
     char *auxstr;
-    double ang_incr, rot_ang;
+    DOUBLE ang_incr, rot_ang;
     int  fold;
-    Matrix2D<double> L(4, 4), R(4, 4);
-    Matrix1D<double> axis(3);
+    Matrix2D<DOUBLE> L(4, 4), R(4, 4);
+    Matrix1D<DOUBLE> axis(3);
     int pgGroup = 0, pgOrder = 0;
     std::vector<std::string> fileContent;
 
@@ -140,11 +140,11 @@ int SymList::read_sym_file(FileName fn_sym)
             auxstr = nextToken();
             fold = textToInteger(auxstr);
             auxstr = nextToken();
-            XX(axis) = textToDouble(auxstr);
+            XX(axis) = textToDOUBLE(auxstr);
             auxstr = nextToken();
-            YY(axis) = textToDouble(auxstr);
+            YY(axis) = textToDOUBLE(auxstr);
             auxstr = nextToken();
-            ZZ(axis) = textToDouble(auxstr);
+            ZZ(axis) = textToDOUBLE(auxstr);
             ang_incr = 360. / fold;
             L.initIdentity();
             for (j = 1, rot_ang = ang_incr; j < fold; j++, rot_ang += ang_incr)
@@ -178,7 +178,7 @@ int SymList::read_sym_file(FileName fn_sym)
             ZZ(axis) = textToFloat(auxstr);
             L.initIdentity();
             L(2, 2) = -1;
-            Matrix2D<double> A;
+            Matrix2D<DOUBLE> A;
             alignWithZ(axis,A);
             A = A.transpose();
             R = A * L * A.inv();
@@ -194,7 +194,7 @@ int SymList::read_sym_file(FileName fn_sym)
 }
 
 // Get matrix ==============================================================
-void SymList::get_matrices(int i, Matrix2D<double> &L, Matrix2D<double> &R)
+void SymList::get_matrices(int i, Matrix2D<DOUBLE> &L, Matrix2D<DOUBLE> &R)
 const
 {
     int k, l;
@@ -209,8 +209,8 @@ const
 }
 
 // Set matrix ==============================================================
-void SymList::set_matrices(int i, const Matrix2D<double> &L,
-                           const Matrix2D<double> &R)
+void SymList::set_matrices(int i, const Matrix2D<DOUBLE> &L,
+                           const Matrix2D<DOUBLE> &R)
 {
     int k, l;
     for (k = 4 * i; k < 4*i + 4; k++)
@@ -222,7 +222,7 @@ void SymList::set_matrices(int i, const Matrix2D<double> &L,
 }
 
 // Add matrix ==============================================================
-void SymList::add_matrices(const Matrix2D<double> &L, const Matrix2D<double> &R,
+void SymList::add_matrices(const Matrix2D<DOUBLE> &L, const Matrix2D<DOUBLE> &R,
                            int chain_length)
 {
     if (MAT_XSIZE(L) != 4 || MAT_YSIZE(L) != 4 || MAT_XSIZE(R) != 4 || MAT_YSIZE(R) != 4)
@@ -272,9 +272,9 @@ bool found_not_tried(const Matrix2D<int> &tried, int &i, int &j,
 //#define DEBUG
 void SymList::compute_subgroup()
 {
-    Matrix2D<double> I(4, 4);
+    Matrix2D<DOUBLE> I(4, 4);
     I.initIdentity();
-    Matrix2D<double> L1(4, 4), R1(4, 4), L2(4, 4), R2(4, 4), newL(4, 4), newR(4, 4);
+    Matrix2D<DOUBLE> L1(4, 4), R1(4, 4), L2(4, 4), R2(4, 4), newL(4, 4), newR(4, 4);
     Matrix2D<int>    tried(true_symNo, true_symNo);
     int i, j;
     int new_chain_length;
@@ -287,7 +287,7 @@ void SymList::compute_subgroup()
         newL = L1 * L2;
         newR = R1 * R2;
         new_chain_length = __chain_length(i) + __chain_length(j);
-        Matrix2D<double> newR3 = newR;
+        Matrix2D<DOUBLE> newR3 = newR;
         newR3.resize(3,3);
         if (newL.isIdentity() && newR3.isIdentity()) continue;
 
@@ -767,7 +767,7 @@ void SymList::fill_symmetry_class(const FileName symmetry, int pgGroup, int pgOr
 void SymList::writeDefinition(std::ostream &outstream, FileName fn_sym)
 {
 	read_sym_file(fn_sym);
-	Matrix2D<double> L(3,3), R(3,3);
+	Matrix2D<DOUBLE> L(3,3), R(3,3);
 	outstream << " ++++ Using symmetry group " << fn_sym << ", with the following " << SymsNo()+1 << " transformation matrices:"<< std::endl;
     R.initIdentity();
     outstream << " R(1)= " << R;
@@ -783,7 +783,7 @@ void SymList::writeDefinition(std::ostream &outstream, FileName fn_sym)
 
 }
 
-double SymList::non_redundant_ewald_sphere(int pgGroup, int pgOrder)
+DOUBLE SymList::non_redundant_ewald_sphere(int pgGroup, int pgOrder)
 {
     if (pgGroup == pg_CN)
     {
@@ -892,7 +892,7 @@ double SymList::non_redundant_ewald_sphere(int pgGroup, int pgOrder)
     }
 }
 
-void symmetriseMap(MultidimArray<double> &img, FileName &fn_sym, bool do_wrap)
+void symmetriseMap(MultidimArray<DOUBLE> &img, FileName &fn_sym, bool do_wrap)
 {
 
 	if (img.getDim() != 3)
@@ -903,8 +903,8 @@ void symmetriseMap(MultidimArray<double> &img, FileName &fn_sym, bool do_wrap)
 	SymList SL;
 	SL.read_sym_file(fn_sym);
 
-	Matrix2D<double> L(4, 4), R(4, 4); // A matrix from the list
-    MultidimArray<double> sum, aux;
+	Matrix2D<DOUBLE> L(4, 4), R(4, 4); // A matrix from the list
+    MultidimArray<DOUBLE> sum, aux;
     sum = img;
     aux.resize(img);
 
diff --git a/src/symmetries.h b/src/symmetries.h
index e0174dd..89b35bc 100644
--- a/src/symmetries.h
+++ b/src/symmetries.h
@@ -134,7 +134,7 @@ class SymList
 {
 public:
     // L and R matrices
-    Matrix2D<double> __L, __R;
+    Matrix2D<DOUBLE> __L, __R;
     Matrix1D<int>    __chain_length;
 
     // As the symmetry elements form a subgroup, this is the number of
@@ -186,7 +186,7 @@ public:
                ...
            }
         @endcode */
-    void get_matrices(int i, Matrix2D<double> &L, Matrix2D<double> &R) const;
+    void get_matrices(int i, Matrix2D<DOUBLE> &L, Matrix2D<DOUBLE> &R) const;
 
     /** Set a couple of matrices in the symmetry list.
         The number of matrices inside the list is given by SymsNo.
@@ -200,7 +200,7 @@ public:
                ...
            }
         @endcode */
-    void set_matrices(int i, const Matrix2D<double> &L, const Matrix2D<double> &R);
+    void set_matrices(int i, const Matrix2D<DOUBLE> &L, const Matrix2D<DOUBLE> &R);
 
     /** Read a symmetry file into a symmetry list.
         The former symmetry list is overwritten with the new one. All the
@@ -218,7 +218,7 @@ public:
 
         The chain length is the number of single matrices multiplication of
         which the inserted one is compound.*/
-    void add_matrices(const Matrix2D<double> &L, const Matrix2D<double> &R,
+    void add_matrices(const Matrix2D<DOUBLE> &L, const Matrix2D<DOUBLE> &R,
                       int chain_length);
 
     /** Compute subgroup for this structure.
@@ -261,12 +261,12 @@ public:
 
     /** Return the area of the non redundant part of the Ewald sphere
     */
-    double  non_redundant_ewald_sphere(int pgGroup, int pgOrder);
+    DOUBLE  non_redundant_ewald_sphere(int pgGroup, int pgOrder);
 };
 
 
 // Symmetrise a 3D map according to the specified symmetry
-void symmetriseMap(MultidimArray<double> &img, FileName &fn_sym, bool do_wrap = false);
+void symmetriseMap(MultidimArray<DOUBLE> &img, FileName &fn_sym, bool do_wrap = false);
 
 //@}
 #endif
diff --git a/src/tabfuncs.cpp b/src/tabfuncs.cpp
index 5c690cb..d2e383f 100644
--- a/src/tabfuncs.cpp
+++ b/src/tabfuncs.cpp
@@ -21,7 +21,7 @@
 
 void TabSine::initialise(const int _nr_elem)
 {
-	sampling = 2 * PI / (double) _nr_elem;
+	sampling = 2 * PI / (DOUBLE) _nr_elem;
 	TabSine::fillTable(_nr_elem);
 }
 //Pre-calculate table values
@@ -30,22 +30,22 @@ void TabSine::fillTable(const int _nr_elem)
 	tabulatedValues.resize(_nr_elem);
 	for (int i = 0; i < _nr_elem; i++)
 	{
-		double xx = (double) i * sampling;
+		DOUBLE xx = (DOUBLE) i * sampling;
 		tabulatedValues(i) = sin(xx);
 	}
 }
 // Value access
-double TabSine::operator()(double val) const
+DOUBLE TabSine::operator()(DOUBLE val) const
 {
 	int idx = (int)( ABS(val) / sampling);
-	double retval = DIRECT_A1D_ELEM(tabulatedValues, idx % XSIZE(tabulatedValues));
+	DOUBLE retval = DIRECT_A1D_ELEM(tabulatedValues, idx % XSIZE(tabulatedValues));
 	return (val < 0 ) ? -retval : retval;
 }
 
 void TabCosine::initialise(const int _nr_elem)
 {
-	sampling = 2 * PI / (double) _nr_elem;
-	TabCosine::fillTable();
+	sampling = 2 * PI / (DOUBLE) _nr_elem;
+	TabCosine::fillTable(_nr_elem);
 }
 //Pre-calculate table values
 void TabCosine::fillTable(const int _nr_elem)
@@ -53,18 +53,18 @@ void TabCosine::fillTable(const int _nr_elem)
 	tabulatedValues.resize(_nr_elem);
 	for (int i = 0; i < _nr_elem; i++)
 	{
-		double xx = (double) i * sampling;
+		DOUBLE xx = (DOUBLE) i * sampling;
 		tabulatedValues(i) = cos(xx);
 	}
 }
 // Value access
-double TabCosine::operator()(double val) const
+DOUBLE TabCosine::operator()(DOUBLE val) const
 {
 	int idx = (int)( ABS(val) / sampling);
 	return DIRECT_A1D_ELEM(tabulatedValues, idx % XSIZE(tabulatedValues));
 }
 
-void TabBlob::initialise(double _radius, double _alpha, int _order, const int _nr_elem)
+void TabBlob::initialise(DOUBLE _radius, DOUBLE _alpha, int _order, const int _nr_elem)
 {
 	radius = _radius;
 	alpha = _alpha;
@@ -78,12 +78,12 @@ void TabBlob::fillTable(const int _nr_elem)
 	tabulatedValues.resize(_nr_elem);
 	for (int i = 0; i < _nr_elem; i++)
 	{
-		double xx = (double) i * sampling;
+		DOUBLE xx = (DOUBLE) i * sampling;
 		tabulatedValues(i) = kaiser_value(xx, radius, alpha, order);
 	}
 }
 // Value access
-double TabBlob::operator()(double val) const
+DOUBLE TabBlob::operator()(DOUBLE val) const
 {
 	int idx = (int)( ABS(val) / sampling);
 	if (idx >= XSIZE(tabulatedValues))
@@ -92,12 +92,12 @@ double TabBlob::operator()(double val) const
 		return DIRECT_A1D_ELEM(tabulatedValues, idx);
 }
 
-void TabFtBlob::initialise(double _radius, double _alpha, int _order, const int _nr_elem)
+void TabFtBlob::initialise(DOUBLE _radius, DOUBLE _alpha, int _order, const int _nr_elem)
 {
 	radius = _radius;
 	alpha = _alpha;
 	order = _order;
-	sampling = 0.5 / (double)_nr_elem;
+	sampling = 0.5 / (DOUBLE)_nr_elem;
 	TabFtBlob::fillTable(_nr_elem);
 }
 //Pre-calculate table values
@@ -106,12 +106,12 @@ void TabFtBlob::fillTable(const int _nr_elem)
 	tabulatedValues.resize(_nr_elem);
 	for (int i = 0; i < _nr_elem; i++)
 	{
-		double xx = (double) i * sampling;
+		DOUBLE xx = (DOUBLE) i * sampling;
 		tabulatedValues(i) = kaiser_Fourier_value(xx, radius, alpha, order);
 	}
 }
 // Value access
-double TabFtBlob::operator()(double val) const
+DOUBLE TabFtBlob::operator()(DOUBLE val) const
 {
 	int idx = (int)( ABS(val) / sampling);
 	if (idx >= XSIZE(tabulatedValues))
diff --git a/src/tabfuncs.h b/src/tabfuncs.h
index 82d7646..3d93efa 100644
--- a/src/tabfuncs.h
+++ b/src/tabfuncs.h
@@ -29,8 +29,8 @@ class TabFunction
 {
 
 protected:
-	MultidimArray<double> tabulatedValues;
-	double  sampling;
+	MultidimArray<DOUBLE> tabulatedValues;
+	DOUBLE  sampling;
 public:
 	// Empty constructor
 	TabFunction() {}
@@ -84,7 +84,7 @@ public:
 	void fillTable(const int _nr_elem = 5000);
 
 	// Value access
-	double operator()(double val) const;
+	DOUBLE operator()(DOUBLE val) const;
 
 };
 
@@ -100,7 +100,7 @@ public:
 	void fillTable(const int _nr_elem = 5000);
 
 	// Value access
-	double operator()(double val) const;
+	DOUBLE operator()(DOUBLE val) const;
 
 };
 
@@ -108,8 +108,8 @@ class TabBlob : public TabFunction
 {
 
 private:
-	double radius;
-	double alpha;
+	DOUBLE radius;
+	DOUBLE alpha;
 	int order;
 
 public:
@@ -117,13 +117,13 @@ public:
 	TabBlob() {}
 
 	// Constructor (with parameters)
-	void initialise(double _radius, double _alpha, int _order, const int _nr_elem = 10000);
+	void initialise(DOUBLE _radius, DOUBLE _alpha, int _order, const int _nr_elem = 10000);
 
 	//Pre-calculate table values
 	void fillTable(const int _nr_elem = 5000);
 
 	// Value access
-	double operator()(double val) const;
+	DOUBLE operator()(DOUBLE val) const;
 
 };
 
@@ -131,8 +131,8 @@ class TabFtBlob : public TabFunction
 {
 
 private:
-	double radius;
-	double alpha;
+	DOUBLE radius;
+	DOUBLE alpha;
 	int order;
 
 public:
@@ -140,13 +140,13 @@ public:
 	TabFtBlob() {}
 
 	 // Constructor (with parameters)
-	void initialise(double _radius, double _alpha, int _order, const int _nr_elem = 10000);
+	void initialise(DOUBLE _radius, DOUBLE _alpha, int _order, const int _nr_elem = 10000);
 
 	//Pre-calculate table values
 	void fillTable(const int _nr_elem = 5000);
 
 	// Value access
-	double operator()(double val) const;
+	DOUBLE operator()(DOUBLE val) const;
 
 };
 
diff --git a/src/transformations.cpp b/src/transformations.cpp
index 2a8baca..ebb521c 100644
--- a/src/transformations.cpp
+++ b/src/transformations.cpp
@@ -46,9 +46,9 @@
 #include "src/transformations.h"
 
 /* Rotation 2D ------------------------------------------------------------- */
-void rotation2DMatrix(double ang, Matrix2D< double > &result, bool homogeneous)
+void rotation2DMatrix(DOUBLE ang, Matrix2D< DOUBLE > &result, bool homogeneous)
 {
-    double cosine, sine;
+    DOUBLE cosine, sine;
 
     ang = DEG2RAD(ang);
     cosine = cos(ang);
@@ -76,8 +76,8 @@ void rotation2DMatrix(double ang, Matrix2D< double > &result, bool homogeneous)
 }
 
 /* Translation 2D ---------------------------------------------------------- */
-void translation2DMatrix(const Matrix1D<double> &v,
-                         Matrix2D< double > &result)
+void translation2DMatrix(const Matrix1D<DOUBLE> &v,
+                         Matrix2D< DOUBLE > &result)
 {
     if (VEC_XSIZE(v) != 2)
         REPORT_ERROR("Translation2D_matrix: vector is not in R2");
@@ -88,7 +88,7 @@ void translation2DMatrix(const Matrix1D<double> &v,
 }
 
 /* Rotation 3D around the system axes -------------------------------------- */
-void rotation3DMatrix(double ang, char axis, Matrix2D< double > &result,
+void rotation3DMatrix(DOUBLE ang, char axis, Matrix2D< DOUBLE > &result,
                       bool homogeneous)
 {
     if (homogeneous)
@@ -99,12 +99,11 @@ void rotation3DMatrix(double ang, char axis, Matrix2D< double > &result,
     else
         result.initZeros(3,3);
 
-    double cosine, sine;
+    DOUBLE cosine, sine;
     ang = DEG2RAD(ang);
     cosine = cos(ang);
     sine = sin(ang);
 
-    result.initZeros();
     switch (axis)
     {
     case 'Z':
@@ -134,7 +133,7 @@ void rotation3DMatrix(double ang, char axis, Matrix2D< double > &result,
 }
 
 /* Align a vector with Z axis */
-void alignWithZ(const Matrix1D<double> &axis, Matrix2D<double>& result,
+void alignWithZ(const Matrix1D<DOUBLE> &axis, Matrix2D<DOUBLE>& result,
                 bool homogeneous)
 {
     if (axis.size() != 3)
@@ -146,11 +145,11 @@ void alignWithZ(const Matrix1D<double> &axis, Matrix2D<double>& result,
     }
     else
         result.initZeros(3,3);
-    Matrix1D<double>  Axis(axis);
+    Matrix1D<DOUBLE>  Axis(axis);
     Axis.selfNormalize();
 
     // Compute length of the projection on YZ plane
-    double proj_mod = sqrt(YY(Axis) * YY(Axis) + ZZ(Axis) * ZZ(Axis));
+    DOUBLE proj_mod = sqrt(YY(Axis) * YY(Axis) + ZZ(Axis) * ZZ(Axis));
     if (proj_mod > XMIPP_EQUAL_ACCURACY)
     {   // proj_mod!=0
         // Build Matrix result, which makes the turning axis coincident with Z
@@ -180,19 +179,19 @@ void alignWithZ(const Matrix1D<double> &axis, Matrix2D<double>& result,
 }
 
 /* Rotation 3D around any axis -------------------------------------------- */
-void rotation3DMatrix(double ang, const Matrix1D<double> &axis,
-                      Matrix2D<double> &result, bool homogeneous)
+void rotation3DMatrix(DOUBLE ang, const Matrix1D<DOUBLE> &axis,
+                      Matrix2D<DOUBLE> &result, bool homogeneous)
 {
     // Compute a matrix which makes the turning axis coincident with Z
     // And turn around this axis
-    Matrix2D<double> A,R;
+    Matrix2D<DOUBLE> A,R;
     alignWithZ(axis,A,homogeneous);
     rotation3DMatrix(ang, 'Z', R, homogeneous);
     result=A.transpose() * R * A;
 }
 
 /* Translation 3D ---------------------------------------------------------- */
-void translation3DMatrix(const Matrix1D<double> &v, Matrix2D<double> &result)
+void translation3DMatrix(const Matrix1D<DOUBLE> &v, Matrix2D<DOUBLE> &result)
 {
     if (VEC_XSIZE(v) != 3)
         REPORT_ERROR("Translation3D_matrix: vector is not in R3");
@@ -204,7 +203,7 @@ void translation3DMatrix(const Matrix1D<double> &v, Matrix2D<double> &result)
 }
 
 /* Scale 3D ---------------------------------------------------------------- */
-void scale3DMatrix(const Matrix1D<double> &sc, Matrix2D<double>& result,
+void scale3DMatrix(const Matrix1D<DOUBLE> &sc, Matrix2D<DOUBLE>& result,
                    bool homogeneous)
 {
     if (VEC_XSIZE(sc) != 3)
diff --git a/src/transformations.h b/src/transformations.h
index 11d37a9..ed40614 100644
--- a/src/transformations.h
+++ b/src/transformations.h
@@ -71,7 +71,7 @@
  *  rotation2DMatrix(60,m);
  * @endcode
  */
-void rotation2DMatrix(double ang, Matrix2D< double > &m, bool homogeneous=true);
+void rotation2DMatrix(DOUBLE ang, Matrix2D< DOUBLE > &m, bool homogeneous=true);
 
 /** Creates a translational matrix (3x3) for images
  * @ingroup GeometricalTransformations
@@ -84,7 +84,7 @@ void rotation2DMatrix(double ang, Matrix2D< double > &m, bool homogeneous=true);
  * m = translation2DMatrix(vectorR2(1, 0));
  * @endcode
  */
-void translation2DMatrix(const Matrix1D< double > &v, Matrix2D< double > &m);
+void translation2DMatrix(const Matrix1D< DOUBLE > &v, Matrix2D< DOUBLE > &m);
 
 /** Creates a rotational matrix (4x4) for volumes around system axis
  * @ingroup GeometricalTransformations
@@ -118,7 +118,7 @@ void translation2DMatrix(const Matrix1D< double > &v, Matrix2D< double > &m);
  * m = rotation3DMatrix(60, 'X');
  * @endcode
  */
-void rotation3DMatrix(double ang, char axis, Matrix2D< double > &m,
+void rotation3DMatrix(DOUBLE ang, char axis, Matrix2D< DOUBLE > &m,
 		bool homogeneous=true);
 
 /** Creates a rotational matrix (4x4) for volumes around any axis
@@ -132,7 +132,7 @@ void rotation3DMatrix(double ang, char axis, Matrix2D< double > &m,
  * m = rotation3DMatrix(60, vectorR3(1, 1, 1));
  * @endcode
  */
-void rotation3DMatrix(double ang, const Matrix1D< double >& axis, Matrix2D< double > &m,
+void rotation3DMatrix(DOUBLE ang, const Matrix1D< DOUBLE >& axis, Matrix2D< DOUBLE > &m,
 		bool homogeneous=true);
 
 /** Matrix which transforms the given axis into Z
@@ -143,14 +143,14 @@ void rotation3DMatrix(double ang, const Matrix1D< double >& axis, Matrix2D< doub
  * order to produce rotational matrices, for instance, around any axis.
  *
  * @code
- * Matrix2D< double > A = alignWithZ(axis);
+ * Matrix2D< DOUBLE > A = alignWithZ(axis);
  * return A.transpose() * rotation3DMatrix(ang, 'Z') * A;
  * @endcode
  *
  * The returned matrix is such that A*axis=Z, where Z and axis are column
  * vectors.
  */
-void alignWithZ(const Matrix1D< double >& axis, Matrix2D< double > &m, bool homogeneous=true);
+void alignWithZ(const Matrix1D< DOUBLE >& axis, Matrix2D< DOUBLE > &m, bool homogeneous=true);
 
 /** Creates a translational matrix (4x4) for volumes
  * @ingroup GeometricalTransformations
@@ -163,7 +163,7 @@ void alignWithZ(const Matrix1D< double >& axis, Matrix2D< double > &m, bool homo
  * m = translation3DMatrix(vectorR3(0, 0, 2));
  * @endcode
  */
-void translation3DMatrix(const Matrix1D< double >& v, Matrix2D< double > &m);
+void translation3DMatrix(const Matrix1D< DOUBLE >& v, Matrix2D< DOUBLE > &m);
 
 /** Creates a scaling matrix (4x4) for volumes
  * @ingroup GeometricalTransformations
@@ -171,13 +171,13 @@ void translation3DMatrix(const Matrix1D< double >& v, Matrix2D< double > &m);
  * The scaling factors for the different axis must be given as a vector. So
  * that, XX(sc)=scale for X axis, YY(sc)=...
  */
-void scale3DMatrix(const Matrix1D< double >& sc, Matrix2D< double > &m,
+void scale3DMatrix(const Matrix1D< DOUBLE >& sc, Matrix2D< DOUBLE > &m,
 		bool homogeneous=true);
 
 /** Applies a geometrical transformation.
  * @ingroup GeometricalTransformations
  *
- * Any geometrical transformation defined by the matrix A (double (4x4)!!
+ * Any geometrical transformation defined by the matrix A (DOUBLE (4x4)!!
  * ie, in homogeneous R3 coordinates) is applied to the volume V1.
  * The result is stored in V2 (it cannot be the same as the input volume).
  * An exception is thrown if the transformation matrix is not 4x4.
@@ -242,7 +242,7 @@ void scale3DMatrix(const Matrix1D< double >& sc, Matrix2D< double > &m,
  * Although you can also use the constants IS_INV, or WRAP.
  *
  * @code
- * Matrix2D< double > A(4,4);
+ * Matrix2D< DOUBLE > A(4,4);
  * A.initIdentity;
  * applyGeometry(V2, A, V1);
  * @endcode
@@ -250,7 +250,7 @@ void scale3DMatrix(const Matrix1D< double >& sc, Matrix2D< double > &m,
 template<typename T>
 void applyGeometry(const MultidimArray<T>& V1,
                    MultidimArray<T>& V2,
-                   const Matrix2D< double > A,
+                   const Matrix2D< DOUBLE > A,
                    bool inv,
                    bool wrap,
                    T outside = 0)
@@ -277,14 +277,14 @@ void applyGeometry(const MultidimArray<T>& V1,
         return;
     }
 
-    Matrix2D<double> Ainv;
-    const Matrix2D<double> * Aptr=&A;
+    Matrix2D<DOUBLE> Ainv;
+    const Matrix2D<DOUBLE> * Aptr=&A;
     if (!inv)
     {
         Ainv = A.inv();
         Aptr=&Ainv;
     }
-    const Matrix2D<double> &Aref=*Aptr;
+    const Matrix2D<DOUBLE> &Aref=*Aptr;
 
     // For scalings the output matrix is resized outside to the final
     // size instead of being resized inside the routine with the
@@ -297,10 +297,10 @@ void applyGeometry(const MultidimArray<T>& V1,
         // 2D transformation
 
         int m1, n1, m2, n2;
-        double x, y, xp, yp;
-        double minxp, minyp, maxxp, maxyp;
+        DOUBLE x, y, xp, yp;
+        DOUBLE minxp, minyp, maxxp, maxyp;
         int cen_x, cen_y, cen_xp, cen_yp;
-        double wx, wy;
+        DOUBLE wx, wy;
         int Xdim, Ydim;
 
         // Find center and limits of image
@@ -424,14 +424,14 @@ void applyGeometry(const MultidimArray<T>& V1,
                         // The same can be said for wy.
                         tmp  = (T)((1 - wy) * (1 - wx) * DIRECT_A2D_ELEM(V1, n1, m1));
 
-                        if (wx != 0 && m2 < V1.xdim)
+                        if (m2 < V1.xdim)
                             tmp += (T)((1 - wy) * wx * DIRECT_A2D_ELEM(V1, n1, m2));
 
-                        if (wy != 0 && n2 < V1.ydim)
+                        if (n2 < V1.ydim)
                         {
                             tmp += (T)(wy * (1 - wx) * DIRECT_A2D_ELEM(V1, n2, m1));
 
-                            if (wx != 0 && m2 < V1.xdim)
+                            if (m2 < V1.xdim)
                                 tmp += (T)(wy * wx * DIRECT_A2D_ELEM(V1, n2, m2));
                         }
 
@@ -455,10 +455,10 @@ void applyGeometry(const MultidimArray<T>& V1,
         // 3D transformation
 
         int m1, n1, o1, m2, n2, o2;
-        double x, y, z, xp, yp, zp;
-        double minxp, minyp, maxxp, maxyp, minzp, maxzp;
+        DOUBLE x, y, z, xp, yp, zp;
+        DOUBLE minxp, minyp, maxxp, maxyp, minzp, maxzp;
         int cen_x, cen_y, cen_z, cen_xp, cen_yp, cen_zp;
-        double wx, wy, wz;
+        DOUBLE wx, wy, wz;
 
         // Find center of MultidimArray
         cen_z = (int)(V2.zdim / 2);
@@ -604,25 +604,25 @@ void applyGeometry(const MultidimArray<T>& V1,
                             // The same can be said for wy.
                             tmp  = (T)((1 - wz) * (1 - wy) * (1 - wx) * DIRECT_A3D_ELEM(V1, o1, n1, m1));
 
-                            if (wx != 0 && m2 < V1.xdim)
+                            if (m2 < V1.xdim)
                                 tmp += (T)((1 - wz) * (1 - wy) * wx * DIRECT_A3D_ELEM(V1, o1, n1, m2));
 
-                            if (wy != 0 && n2 < V1.ydim)
+                            if (n2 < V1.ydim)
                             {
                                 tmp += (T)((1 - wz) * wy * (1 - wx) * DIRECT_A3D_ELEM(V1, o1, n2, m1));
-                                if (wx != 0 && m2 < V1.xdim)
+                                if (m2 < V1.xdim)
                                     tmp += (T)((1 - wz) * wy * wx * DIRECT_A3D_ELEM(V1, o1, n2, m2));
                             }
 
-                            if (wz != 0 && o2 < V1.zdim)
+                            if (o2 < V1.zdim)
                             {
                                 tmp += (T)(wz * (1 - wy) * (1 - wx) * DIRECT_A3D_ELEM(V1, o2, n1, m1));
-                                if (wx != 0 && m2 < V1.xdim)
+                                if (m2 < V1.xdim)
                                     tmp += (T)(wz * (1 - wy) * wx * DIRECT_A3D_ELEM(V1, o2, n1, m2));
-                                if (wy != 0 && n2 < V1.ydim)
+                                if (n2 < V1.ydim)
                                 {
                                     tmp += (T)(wz * wy * (1 - wx) * DIRECT_A3D_ELEM(V1, o2, n2, m1));
-                                    if (wx != 0 && m2 < V1.xdim)
+                                    if (m2 < V1.xdim)
                                         tmp += (T)(wz * wy * wx * DIRECT_A3D_ELEM(V1, o2, n2, m2));
                                 }
                             }
@@ -675,7 +675,7 @@ void applyGeometry(const MultidimArray<T>& V1,
  */
 template<typename T>
 void selfApplyGeometry(MultidimArray<T>& V1,
-                       const Matrix2D< double > A, bool inv,
+                       const Matrix2D< DOUBLE > A, bool inv,
                        bool wrap, T outside = 0)
 {
     MultidimArray<T> aux = V1;
@@ -697,10 +697,10 @@ void selfApplyGeometry(MultidimArray<T>& V1,
 template<typename T>
 void rotate(const MultidimArray<T>& V1,
             MultidimArray<T>& V2,
-            double ang, char axis = 'Z',
+            DOUBLE ang, char axis = 'Z',
             bool wrap = DONT_WRAP, T outside = 0)
 {
-    Matrix2D< double > tmp;
+    Matrix2D< DOUBLE > tmp;
     if (V1.getDim()==2)
     {
         rotation2DMatrix(ang,tmp);
@@ -722,7 +722,7 @@ void rotate(const MultidimArray<T>& V1,
  */
 template<typename T>
 void selfRotate(MultidimArray<T>& V1,
-                double ang, char axis = 'Z',
+                DOUBLE ang, char axis = 'Z',
                 bool wrap = DONT_WRAP, T outside = 0)
 {
     MultidimArray<T> aux = V1;
@@ -743,10 +743,10 @@ void selfRotate(MultidimArray<T>& V1,
 template<typename T>
 void translate(const MultidimArray<T> &V1,
                MultidimArray<T> &V2,
-               const Matrix1D< double >& v,
+               const Matrix1D< DOUBLE >& v,
                bool wrap = WRAP, T outside = 0)
 {
-    Matrix2D< double > tmp;
+    Matrix2D< DOUBLE > tmp;
     if (V1.getDim()==2)
         translation2DMatrix(v, tmp);
     else if (V1.getDim()==3)
@@ -764,7 +764,7 @@ void translate(const MultidimArray<T> &V1,
  */
 template<typename T>
 void selfTranslate(MultidimArray<T>& V1,
-                   const Matrix1D< double >& v,
+                   const Matrix1D< DOUBLE >& v,
                    bool wrap = WRAP, T outside = 0)
 {
     MultidimArray<T> aux = V1;
@@ -784,10 +784,10 @@ void translateCenterOfMassToCenter(const MultidimArray<T> &V1,
 {
     V2 = V1;
     V2.setXmippOrigin();
-    Matrix1D< double > center;
+    Matrix1D< DOUBLE > center;
     V2.centerOfMass(center);
     center *= -1;
-    translate(V1, V2, center, wrap, 0.);
+    translate(V1, V2, center, wrap);
 }
 
 /** Translate center of mass to center
@@ -820,20 +820,20 @@ void scaleToSize(const MultidimArray<T> &V1,
                  int Xdim, int Ydim, int Zdim = 1)
 {
 
-    Matrix2D< double > tmp;
+    Matrix2D< DOUBLE > tmp;
     if (V1.getDim()==2)
     {
         tmp.initIdentity(3);
-        tmp(0, 0) = (double) Xdim / (double) XSIZE(V1);
-        tmp(1, 1) = (double) Ydim / (double) YSIZE(V1);
+        tmp(0, 0) = (DOUBLE) Xdim / (DOUBLE) XSIZE(V1);
+        tmp(1, 1) = (DOUBLE) Ydim / (DOUBLE) YSIZE(V1);
         V2.resize(1, 1, Ydim, Xdim);
     }
     else if (V1.getDim()==3)
     {
         tmp.initIdentity(4);
-        tmp(0, 0) = (double) Xdim / (double) XSIZE(V1);
-        tmp(1, 1) = (double) Ydim / (double) YSIZE(V1);
-        tmp(2, 2) = (double) Zdim / (double) ZSIZE(V1);
+        tmp(0, 0) = (DOUBLE) Xdim / (DOUBLE) XSIZE(V1);
+        tmp(1, 1) = (DOUBLE) Ydim / (DOUBLE) YSIZE(V1);
+        tmp(2, 2) = (DOUBLE) Zdim / (DOUBLE) ZSIZE(V1);
         V2.resize(1, Zdim, Ydim, Xdim);
     }
     else
@@ -882,7 +882,7 @@ void radialAverage(const MultidimArray< T >& m,
                    MultidimArray< int >& radial_count,
                    const bool& rounding = false)
 {
-    Matrix1D< double > idx(3);
+    Matrix1D< DOUBLE > idx(3);
 
     // If center_of_rot was written for 2D image
     if (center_of_rot.size() < 3)
@@ -892,9 +892,9 @@ void radialAverage(const MultidimArray< T >& m,
     // dimension of the radial average vector
     MultidimArray< int > distances(8);
 
-    double z = STARTINGZ(m) - ZZ(center_of_rot);
-    double y = STARTINGY(m) - YY(center_of_rot);
-    double x = STARTINGX(m) - XX(center_of_rot);
+    DOUBLE z = STARTINGZ(m) - ZZ(center_of_rot);
+    DOUBLE y = STARTINGY(m) - YY(center_of_rot);
+    DOUBLE x = STARTINGX(m) - XX(center_of_rot);
 
     distances(0) = (int) floor(sqrt(x * x + y * y + z * z));
     x = FINISHINGX(m) - XX(center_of_rot);

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/relion.git



More information about the debian-med-commit mailing list