From d2422b5a71b5ff9a54287c60432f5c267467882f Mon Sep 17 00:00:00 2001
From: Nicola Vigano <nicola.vigano@esrf.fr>
Date: Thu, 30 Apr 2015 14:18:46 +0200
Subject: [PATCH] 6D-Reconstruction: added initial twin reconstruction

Signed-off-by: Nicola Vigano <nicola.vigano@esrf.fr>
---
 .../gtReconstruct6DLaunchAlgorithm.m          |   2 +-
 .../gtReconstructGrainTwinCluster.m           | 111 ++++
 .../Gt6DReconstructionAlgorithmFactory.m      | 116 +++-
 .../gt6DCreateProjDataFromTwinnedGrain.m      | 606 ++++++++++++++++++
 zUtil_Deformation/test_generation/do6D.m      |   4 +-
 5 files changed, 820 insertions(+), 19 deletions(-)
 create mode 100644 5_reconstruction/gtReconstructGrainTwinCluster.m
 create mode 100644 zUtil_Deformation/gt6DCreateProjDataFromTwinnedGrain.m

diff --git a/5_reconstruction/gtReconstruct6DLaunchAlgorithm.m b/5_reconstruction/gtReconstruct6DLaunchAlgorithm.m
index 2ac1d85b..890031b5 100644
--- a/5_reconstruction/gtReconstruct6DLaunchAlgorithm.m
+++ b/5_reconstruction/gtReconstruct6DLaunchAlgorithm.m
@@ -5,7 +5,7 @@ function [algo, good_or] = gtReconstruct6DLaunchAlgorithm(sampler, rec_opts, par
     rec_factory = Gt6DReconstructionAlgorithmFactory(parameters, ...
         'volume_downscaling', rec_opts.volume_downscaling, ...
         'rspace_oversize', rec_opts.rspace_oversize );
-    [algo, good_or] = rec_factory.getSubBlobReconstructionAlgo(sampler, rec_opts.num_interp);
+    [algo, good_or] = rec_factory.getReconstructionAlgo(sampler, rec_opts.num_interp);
 
     mem_cons = [ ...
         algo.get_peak_memory_consumption(rec_opts.algorithm), ...
diff --git a/5_reconstruction/gtReconstructGrainTwinCluster.m b/5_reconstruction/gtReconstructGrainTwinCluster.m
new file mode 100644
index 00000000..e7bf3881
--- /dev/null
+++ b/5_reconstruction/gtReconstructGrainTwinCluster.m
@@ -0,0 +1,111 @@
+function gtReconstructGrainTwinCluster(grain_ids, phase_id, parameters, varargin)
+% gtReconstructGrainOrientation  6D reconstructions on a GPU machine
+%     gtAstraReconstructGrain(grainIDs, phaseID, [parameters])
+%     -------------------------------------------------------
+    if (~exist('parameters', 'var') || isempty(parameters))
+        parameters = gtLoadParameters();
+    end
+    parameters.fsim.check_spot = false;
+
+    rec_opts = gtReconstruct6DGetParamenters(parameters);
+
+    phase_dir = fullfile(parameters.acq.dir, '4_grains', ...
+        sprintf('phase_%02d', phase_id));
+    grains_str_ids = sprintf('_%04d', grain_ids);
+    grain_cluster_file = fullfile(phase_dir, ...
+        sprintf('grain_twin_cluster%s.mat', grains_str_ids));
+
+    fprintf('Loading the cluster file..')
+    grs = load(grain_cluster_file);
+    grs = grs.samp_ors;
+    fprintf('\b\b: Done.\n')
+    num_grains = numel(grs);
+
+    for ii_g = 1:num_grains
+        bb_gvdm = cat(1, grs(ii_g).bb_ors(:).R_vector)';
+
+        sampler(ii_g) = GtOrientationSampling(parameters, grs(ii_g)); %#ok<AGROW>
+        sampler(ii_g).make_even_simple_grid('cubic', rec_opts.grid_edge, bb_gvdm, 1)
+        if (rec_opts.super_sampling > 1)
+            sampler(ii_g).make_supersampling_simple_grid([1 2 3], rec_opts.super_sampling);
+        end
+    end
+
+    [algo, good_or] = gtReconstruct6DLaunchAlgorithm(sampler, rec_opts, parameters);
+
+    vols = algo.getCurrentSolution();
+
+    % Restoring initial volume size (depending on the rounding)
+    if (rec_opts.volume_downscaling > 1)
+        fprintf('Expanding volumes..')
+        c = tic();
+        vols = gtMathsUpsampleVolume(vols, rec_opts.volume_downscaling);
+        fprintf('\b\b: Done (%f seconds).\n', toc(c))
+    end
+
+    ranges = zeros(num_grains, 2);
+    ranges(1, 1) = 1;
+    for ii_g = 2:num_grains
+        ranges(ii_g, 1) = ranges(ii_g-1) + sampler(ii_g-1).get_total_num_orientations();
+    end
+    ranges(1:end-1, 2) = ranges(2:end, 1) - 1;
+    ranges(end, 2) = numel(vols);
+
+    for ii_g = num_grains:-1:1
+        or_sizes = sampler(ii_g).get_orientation_sampling_size();
+        gr_vols = vols(ranges(ii_g, 1):ranges(ii_g, 2));
+        gr_good_or = good_or(ranges(ii_g, 1):ranges(ii_g, 2));
+
+        [avg_R_vecs, avg_R_vecs_int] = sampler(ii_g).getAverageOrientations(gr_vols, gr_good_or);
+        avg_R_vec = sampler(ii_g).getAverageOrientation(gr_vols, gr_good_or);
+        s_g_odf = reshape(sampler(ii_g).getODF(gr_vols, gr_good_or), or_sizes{1});
+
+        vol_size = size(avg_R_vecs_int);
+        shift = gtFwdSimComputeVolumeShifts(grs(ii_g).proj, parameters, vol_size);
+
+        [kam, gam(ii_g)] = gtDefComputeKernelAverageMisorientation(avg_R_vecs, avg_R_vecs_int);
+        [igm, gos(ii_g)] = gtDefComputeIntraGranularMisorientation(avg_R_vecs, avg_R_vecs_int, 'R_vector', grs(ii_g).R_vector);
+
+        ODF6D(ii_g) = struct( ...
+            'voxels_avg_R_vectors', {avg_R_vecs}, ...
+            'intensity', {avg_R_vecs_int}, ...
+            'shift', {shift}, ...
+            'R_vectors', {sampler(ii_g).get_R_vectors()}, ...
+            'single_grain_ODF', {s_g_odf}, ...
+            'single_grain_avg_R_vector', {avg_R_vec}, ...
+            'kernel_average_misorientation', {kam}, ...
+            'intra_granular_misorientation', {igm} );
+    end
+
+    cluster_rec_file = fullfile(phase_dir, ...
+        sprintf('grain_twin_cluster_details%s.mat', grains_str_ids));
+    % Saving and cleaning at the same time
+    if (exist(cluster_rec_file, 'file'))
+        cl_rec = load(cluster_rec_file);
+        cl_rec.ODF6D = ODF6D;
+    else
+        cl_rec = struct('ODF6D', ODF6D); %#ok<NASGU>
+    end
+    save(cluster_rec_file, '-struct', 'cl_rec', '-v7.3');
+
+    if (algo.verbose)
+        clear('ODF6D')
+        [proj_blobs, proj_spots] = algo.getProjectionOfCurrentSolution();
+
+        for ii_g = num_grains:-1:1
+            gr_vols = vols(ranges(ii_g, 1):ranges(ii_g, 2));
+
+            ODF6D(ii_g) = struct( ...
+                'orientation_volumes', {gr_vols}, ...
+                'fwd_projected_blobs', {proj_blobs}, ...
+                'fwd_projected_spots', {proj_spots}, ...
+                'grain_average_misorientation', {gam(ii_g)}, ...
+                'grain_orientation_spread', {gos(ii_g)}, ...
+                'compute_statistics', {algo.get_statistics()} );
+        end
+        grain_full_details_file = fullfile(phase_dir, ...
+            sprintf('grain_twin_cluster_full_details%s.mat', grains_str_ids));
+        save(grain_full_details_file, 'ODF6D', '-v7.3');
+    end
+end
+
diff --git a/zUtil_Deformation/Gt6DReconstructionAlgorithmFactory.m b/zUtil_Deformation/Gt6DReconstructionAlgorithmFactory.m
index 03743b0b..fc8a06e6 100644
--- a/zUtil_Deformation/Gt6DReconstructionAlgorithmFactory.m
+++ b/zUtil_Deformation/Gt6DReconstructionAlgorithmFactory.m
@@ -21,7 +21,15 @@ classdef Gt6DReconstructionAlgorithmFactory < handle
             self.parameters = parameters;
         end
 
-        function [algo, good_orients, blobs] = getSubBlobReconstructionAlgo(self, sampler, num_interp, varargin)
+        function [algo, good_orients, blobs] = getReconstructionAlgo(self, sampler, num_interp, varargin)
+            if (numel(sampler) == 1)
+                [algo, good_orients, blobs] = self.getGrainReconstructionAlgo(sampler, num_interp, varargin{:});
+            else
+                [algo, good_orients, blobs] = self.getTwinReconstructionAlgo(sampler, num_interp, varargin{:});
+            end
+        end
+
+        function [algo, good_orients, blobs] = getGrainReconstructionAlgo(self, sampler, num_interp, varargin)
             algo_params = self.get_algorithm_params(sampler, num_interp);
 
             % Build Empty volumes
@@ -31,19 +39,6 @@ classdef Gt6DReconstructionAlgorithmFactory < handle
 
             if (self.volume_downscaling > 1)
                 volume_size = ceil(volume_size / self.volume_downscaling);
-                num_geoms = numel(algo_params.geometries);
-                for ii = 1:num_geoms
-                    algo_params.geometries{ii}(:, 4:12) = algo_params.geometries{ii}(:, 4:12) / self.volume_downscaling;
-                end
-                if (~isempty(algo_params.geometries_ss))
-                    for ii = 1:num_geoms
-                        geom_ss = algo_params.geometries_ss{ii};
-                        for ii_ss = 1:numel(geom_ss)
-                            geom_ss{ii_ss}(:, 4:12) = geom_ss{ii_ss}(:, 4:12) / self.volume_downscaling;
-                        end
-                        algo_params.geometries_ss{ii} = geom_ss;
-                    end
-                end
             end
 
             good_orients = algo_params.good_orients;
@@ -60,6 +55,78 @@ classdef Gt6DReconstructionAlgorithmFactory < handle
                 'psf', algo_params.psf, ...
                 varargin{:} );
         end
+
+        function [algo, good_orients, blobs] = getTwinReconstructionAlgo(self, sampler, num_interp, varargin)
+            num_grains = numel(sampler);
+            for ii_g = 1:num_grains
+                algo_params(ii_g) = self.get_algorithm_params(sampler(ii_g), num_interp); %#ok<AGROW>
+            end
+
+            % Build Empty volumes
+            ref_gr = sampler(1).get_reference_grain();
+            spacing = mean([ref_gr.proj.vol_size_y, ref_gr.proj.vol_size_x, ref_gr.proj.vol_size_z]) * (self.rspace_oversize - 1);
+            volume_size = round([ref_gr.proj.vol_size_y, ref_gr.proj.vol_size_x, ref_gr.proj.vol_size_z] + spacing);
+
+            if (self.volume_downscaling > 1)
+                volume_size = ceil(volume_size / self.volume_downscaling);
+            end
+
+            blobs = cat(1, algo_params(:).blobs);
+            psf = cat(1, algo_params(:).psf);
+
+            good_orients = cat(1, algo_params(:).good_orients);
+
+            geometries = cat(1, algo_params(:).geometries);
+            geometries_ss = cat(1, algo_params(:).geometries_ss);
+
+            offsets = algo_params(1).offsets;
+            offsets_ss = algo_params(1).offsets_ss;
+            base_offset_shift = numel(algo_params(1).blobs);
+            for ii_g = 2:num_grains
+                temp_gr = sampler(ii_g).get_reference_grain();
+                shared = temp_gr.proj.shared_parent(sampler(ii_g).selected);
+
+                orient_offsets = algo_params(ii_g).offsets;
+                for ii_o = 1:numel(orient_offsets)
+                    to_be_summed = ~shared(orient_offsets{ii_o}.blob_offsets);
+                    orient_offsets{ii_o}.blob_offsets(to_be_summed) ...
+                        = orient_offsets{ii_o}.blob_offsets(to_be_summed) + base_offset_shift;
+                    for ii_p = 1:numel(orient_offsets{ii_o}.proj)
+                        ii_b = orient_offsets{ii_o}.proj(ii_p).blob_offset;
+                        if (~temp_gr.proj.shared_parent(ii_b))
+                            orient_offsets{ii_o}.proj(ii_p).blob_offset ...
+                                = ii_b + base_offset_shift;
+                        end
+                    end
+                end
+                offsets = cat(1, offsets, orient_offsets);
+
+                if (~isempty(geometries_ss))
+                    orient_offsets_ss = algo_params(ii_g).offsets_ss;
+                    for ii_o = 1:numel(orient_offsets_ss)
+                        for ii_ss = 1:numel(orient_offsets_ss{ii_o})
+                            to_be_summed = ~shared(orient_offsets{ii_o}{ii_ss}.blob_offsets);
+                            orient_offsets_ss{ii_o}{ii_ss}.blob_offsets(to_be_summed) ...
+                                = orient_offsets_ss{ii_o}{ii_ss}.blob_offsets(to_be_summed) + base_offset_shift;
+                        end
+                    end
+                    offsets_ss = cat(1, offsets_ss, orient_offsets_ss);
+                end
+
+                base_offset_shift = base_offset_shift + numel(algo_params(ii_g).blobs);
+            end
+
+            algo = Gt6DBlobReconstructor(volume_size, blobs, ...
+                'data_type', self.data_type, ...
+                'geometries', geometries, ...
+                'offsets', offsets, ...
+                'ss_geometries', geometries_ss, ...
+                'ss_offsets', offsets_ss, ...
+                'use_astra_projectors', true, ...
+                'volume_ss', self.volume_downscaling, ...
+                'psf', psf, ...
+                varargin{:} );
+        end
     end
 
     methods (Access = protected)
@@ -246,6 +313,21 @@ classdef Gt6DReconstructionAlgorithmFactory < handle
                 offsets_ss = {};
             end
 
+            if (self.volume_downscaling > 1)
+                for ii = 1:num_geoms
+                    geometries{ii}(:, 4:12) = geometries{ii}(:, 4:12) / self.volume_downscaling;
+                end
+                if (~isempty(geometries_ss))
+                    for ii = 1:num_geoms
+                        geom_ss = geometries_ss{ii};
+                        for ii_ss = 1:numel(geom_ss)
+                            geom_ss{ii_ss}(:, 4:12) = geom_ss{ii_ss}(:, 4:12) / self.volume_downscaling;
+                        end
+                        geometries_ss{ii} = geom_ss;
+                    end
+                end
+            end
+
             if (isfield(bl, 'psf'))
                 psf = arrayfun(@(x){ permute(x.psf, [1 3 2]) }, bl);
             elseif (isfield(sampler.ref_gr.proj, 'psf'))
@@ -261,7 +343,7 @@ classdef Gt6DReconstructionAlgorithmFactory < handle
                 'geometries_ss', {geometries_ss}, ...
                 'offsets_ss', {offsets_ss}, ...
                 'psf', {psf}, ...
-                'good_orients', {good_orients} );
+                'good_orients', {good_orients'} );
         end
 
         function [sub_blob_slices, proj_coeffs] = get_sub_blobs(self, blobs, slices_interp, padding)
@@ -441,6 +523,8 @@ classdef Gt6DReconstructionAlgorithmFactory < handle
             else
                 geometries = {grains_props(:).proj_geom};
             end
+            geometries = reshape(geometries, [], 1);
+
             num_geoms = numel(geometries);
 
             w_int_tab(:, :, 1) = floor(w_tab);
@@ -451,7 +535,7 @@ classdef Gt6DReconstructionAlgorithmFactory < handle
             slice_pos_in_blob = w_int_tab - repmat(extreemes_blobs_w(:, 1), [1 num_geoms 2]) + 1;
 
             % Assign offsets
-            offsets = cell(1, num_geoms);
+            offsets = cell(num_geoms, 1);
             for ii = 1:num_geoms
                 valid_proj_dirs = ...
                     (w_tab(:, ii) >= abs_lims(:, 1)) ...
diff --git a/zUtil_Deformation/gt6DCreateProjDataFromTwinnedGrain.m b/zUtil_Deformation/gt6DCreateProjDataFromTwinnedGrain.m
new file mode 100644
index 00000000..c7f51d01
--- /dev/null
+++ b/zUtil_Deformation/gt6DCreateProjDataFromTwinnedGrain.m
@@ -0,0 +1,606 @@
+function [samp_ors, estim_space_bbox_pix, estim_orient_bbox] = gt6DCreateProjDataFromTwinnedGrain(grs_list, phase_id, varargin)
+% FUNCTION [refor] = gt6DCreateProjDataFromGrainCluster(grs_list, phase_id, varargin)
+%   proj: is a grain.proj structure
+%   refor: is a grain structure for the average orientation in the average
+%       point
+%   or: is a set of grain structures for the extreeme corners of the
+%       orientation space that should be considered
+
+    if (~exist('phase_id', 'var'))
+        phase_id = 1;
+    end
+
+    conf = struct( ...
+        'verbose', false, ...
+        'min_eta', 15, ...
+        'ospace_oversize', 1.1, ...
+        'rspace_oversize', 1.1, ...
+        'flat_normalization', true, ...
+        'check_twin_spots', false, ...
+        'mask_spots', true, ...
+        'oversize', 1.4, ...
+        'save', false );
+    conf = parse_pv_pairs(conf, varargin);
+
+    num_grains = numel(grs_list);
+    if (~isstruct(grs_list))
+        fprintf('Loading grains: ')
+        for ii_g = num_grains:-1:1
+            num_chars = fprintf('%02d/%02d (%d)', num_grains-ii_g+1, num_grains, grs_list(ii_g));
+            grs(ii_g) = gtLoadGrain(phase_id, grs_list(ii_g));
+            fprintf(repmat('\b', [1 num_chars]));
+            fprintf(repmat(' ', [1 num_chars]));
+            fprintf(repmat('\b', [1 num_chars]));
+        end
+        fprintf('Done.\n')
+    else
+        grs = grs_list;
+        grs_list = cat(2, grs(:).id);
+    end
+    p = gtLoadParameters();
+    symm = gtCrystGetSymmetryOperators(p.cryst(phase_id).crystal_system);
+
+    if (conf.verbose)
+        fprintf('R_vectors:\n')
+        cat(1, grs(:).R_vector)
+        fprintf('Reciprocal disorientations:\n')
+        for ii_g_1 = 1:num_grains
+            grs_to_check = (ii_g_1+1):num_grains;
+            if (numel(grs_to_check) > 0)
+                fprintf(' - Disorientations from grain: %d\n', grs(ii_g_1).id)
+                for ii_g_2 = grs_to_check
+                    dis_angle = gtDisorientation(grs(ii_g_1).R_vector', grs(ii_g_2).R_vector', symm);
+                    fprintf('   + Grain %d: %f\n', grs(ii_g_2).id, dis_angle);
+                end
+            end
+        end
+    end
+
+    refgr = grs(1);
+    grs(2:end) = reorder_reflections(grs(2:end), refgr);
+    twingr = grs(2);
+
+    if (isfield(refgr.proj, 'ondet'))
+        ref_ondet = refgr.proj.ondet;
+        ref_included = refgr.proj.included;
+        ref_selected = refgr.proj.selected;
+
+        twin_ondet = twingr.proj.ondet;
+        twin_inc = twingr.proj.included;
+        twin_sel = twingr.proj.selected;
+    else
+        ref_ondet = refgr.ondet;
+        ref_included = refgr.included;
+        ref_selected = refgr.selected;
+
+        twin_ondet = twingr.ondet;
+        twin_inc = twingr.included;
+        twin_sel = twingr.selected;
+    end
+
+    if (conf.verbose)
+        try
+            refgr = gtComputeIncomingBeamIntensity(refgr, p);
+            beam_ints = refgr.beam_intensity(ref_selected);
+            beam_ints = beam_ints / mean(beam_ints);
+        catch mexc
+            gtPrintException(mexc, 'Skipping beam intensity renormalization')
+            beam_ints = 1;
+        end
+        blob_ints = refgr.intensity(ref_selected);
+
+        vis_spots = ref_ondet(ref_included(ref_selected));
+        thetatypes = refgr.allblobs.thetatype(vis_spots);
+
+        if (isfield(p.cryst(phase_id), 'int_exp'))
+            predicted_ints = p.cryst(phase_id).int_exp(thetatypes)';
+
+            f = figure();
+            ax = axes('parent', f);
+            plot(ax, blob_ints)
+            hold(ax, 'on')
+            plot(ax, predicted_ints / mean(predicted_ints) * mean(blob_ints), 'y')
+            predicted_ints = predicted_ints .* refgr.allblobs.lorentzfac(vis_spots);
+            plot(ax, predicted_ints / mean(predicted_ints) * mean(blob_ints), 'r')
+            predicted_ints = predicted_ints .* beam_ints;
+            plot(ax, predicted_ints / mean(predicted_ints) * mean(blob_ints), 'g')
+        end
+
+        predicted_ints = p.cryst(phase_id).int(thetatypes)';
+
+        f = figure();
+        ax = axes('parent', f);
+        plot(ax, blob_ints)
+        hold(ax, 'on')
+        plot(ax, predicted_ints / mean(predicted_ints) * mean(blob_ints), 'y')
+        predicted_ints = predicted_ints .* refgr.allblobs.lorentzfac(vis_spots);
+        plot(ax, predicted_ints / mean(predicted_ints) * mean(blob_ints), 'r')
+        predicted_ints = predicted_ints .* beam_ints;
+        plot(ax, predicted_ints / mean(predicted_ints) * mean(blob_ints), 'g')
+
+        fprintf('%d) %d, %g\n', ...
+            [(1:numel(blob_ints))', refgr.allblobs.thetatype(vis_spots), ...
+            blob_ints - predicted_ints / mean(predicted_ints) * mean(blob_ints)]')
+        hold(ax, 'off')
+    end
+
+    % let's now filter the reflections that are not available for the twin
+    bool_ref_ondet = false(size(refgr.allblobs.omega));
+    bool_ref_ondet(ref_ondet) = true;
+    bool_ref_inc = false(size(refgr.allblobs.omega));
+    bool_ref_inc(ref_ondet(ref_included)) = true;
+    bool_ref_sel = false(size(refgr.allblobs.omega));
+    bool_ref_sel(ref_ondet(ref_included(ref_selected))) = true;
+
+    bool_twin_ondet = false(size(refgr.allblobs.omega));
+    bool_twin_ondet(twin_ondet) = true;
+    bool_twin_inc = false(size(refgr.allblobs.omega));
+    bool_twin_inc(twin_ondet(twin_inc)) = true;
+    bool_twin_sel = false(size(refgr.allblobs.omega));
+    bool_twin_sel(twin_ondet(twin_inc(twin_sel))) = true;
+
+%     bool_deleted_ref_ondet = bool_ref_ondet & ~bool_twin_ondet;
+%     bool_deleted_ref_inc = bool_ref_inc & ~bool_twin_ondet;
+% 
+%     ref_deleted_ondet = find(bool_deleted_ref_ondet);
+%     ref_deleted_included = find(bool_deleted_ref_inc(ref_deleted_ondet));
+
+    bool_ref_ondet = bool_ref_ondet & bool_twin_ondet;
+    bool_ref_inc = bool_ref_inc & bool_twin_ondet;
+    bool_ref_sel = bool_ref_sel & bool_twin_ondet;
+
+%     bool_twin_ondet = bool_ref_ondet & bool_twin_ondet;
+%     bool_twin_inc = bool_ref_inc & bool_twin_inc;
+%     bool_twin_sel = bool_ref_sel & bool_twin_sel;
+
+    ref_ondet = find(bool_ref_ondet);
+    ref_included = find(bool_ref_inc(ref_ondet));
+    ref_selected = bool_ref_sel(ref_ondet(ref_included));
+
+    if (isfield(refgr.proj, 'ondet'))
+        refgr.proj.ondet = ref_ondet;
+        refgr.proj.included = ref_included;
+        refgr.proj.selected = ref_selected;
+    else
+        refgr.ondet = ref_ondet;
+        refgr.included = ref_included;
+        refgr.selected = ref_selected;
+    end
+
+%     ref_twin_ondet_bool = bool_twin_ondet(ref_ondet);
+%     ref_twin_included_bool = bool_twin_inc(ref_ondet(ref_included));
+%     ref_twin_selected_bool = bool_twin_sel(ref_ondet(ref_included(ref_selected)));
+
+    % And now..
+    fwdsim_inc_refl = ref_ondet(ref_included);
+
+    if (conf.verbose)
+        vis_sel = fwdsim_inc_refl;
+%         vis_sel = ref_ondet;
+        fprintf('%02d) w1 %6.2f, w2 %6.2f, w_inds (%d<->%d), hkls [%2d %2d %2d]<->[%2d %2d %2d] <- diff: %7.2f (!! %d) [twin: i %d, s %d]\n', ...
+            [(1:numel(vis_sel))', ...
+            grs(1).allblobs.omega(vis_sel), ...
+            grs(2).allblobs.omega(vis_sel), ...
+            grs(1).allblobs.omind(vis_sel), ...
+            grs(2).allblobs.omind(vis_sel), ...
+            grs(1).allblobs.hklsp(vis_sel, [1 2 4]), ...
+            grs(2).allblobs.hklsp(vis_sel, [1 2 4]), ...
+            grs(1).allblobs.omega(vis_sel) - grs(2).allblobs.omega(vis_sel), ...
+            abs(grs(1).allblobs.omega(vis_sel) - grs(2).allblobs.omega(vis_sel)) < 1, ...
+            bool_twin_inc(vis_sel), bool_twin_sel(vis_sel)]');
+    end
+
+    vol_half_size = [refgr.proj.vol_size_y, refgr.proj.vol_size_x, refgr.proj.vol_size_z] / 2;
+    space_bbox = [ ...
+            refgr.proj.centerpix - vol_half_size * conf.rspace_oversize, ...
+            refgr.proj.centerpix + vol_half_size * conf.rspace_oversize ];
+    estim_space_bbox_pix = [floor(space_bbox(1:3)), ceil(space_bbox(4:6))];
+    bbox_size_pix = estim_space_bbox_pix(4:6) - estim_space_bbox_pix(1:3);
+
+    estim_space_bbox_mm = [ ...
+        gtGeoSam2Sam(estim_space_bbox_pix(1:3), p.recgeo, p.samgeo, false, false), ...
+        gtGeoSam2Sam(estim_space_bbox_pix(4:6), p.recgeo, p.samgeo, false, false) ];
+    bbox_size_mm = estim_space_bbox_mm(4:6) - estim_space_bbox_mm(1:3);
+
+    img_bboxes = cell(num_grains, 1);
+    img_sizes = cell(num_grains, 1);
+
+    for ii_g = num_grains:-1:1
+        sampler = GtOrientationSampling(p, grs(ii_g), 'verbose', conf.verbose);
+        r_vecs = sampler.guess_ODF_BB()';
+
+        estim_orient_bbox = [min(r_vecs, [], 1), max(r_vecs, [], 1)];
+        bbox_size_rod = estim_orient_bbox(4:6) - estim_orient_bbox(1:3);
+
+        % oversizing the orienation a bit
+        delta_bbox_size_rod = bbox_size_rod * 0.05;
+        estim_orient_bbox = estim_orient_bbox + [-delta_bbox_size_rod, delta_bbox_size_rod];
+        bbox_size_rod = estim_orient_bbox(4:6) - estim_orient_bbox(1:3);
+
+        bbox_size_deg = 2 * atand(bbox_size_rod);
+
+        if (conf.verbose)
+            fprintf('\n');
+            fprintf('Estimated spatial voxel BBox: [%3d, %3d, %3d] -> [%3d, %3d, %3d]\n', estim_space_bbox_pix);
+            fprintf('                   BBox size: %3d, %3d, %3d (%f, %f, %f mm)\n', bbox_size_pix, bbox_size_mm);
+
+            fprintf('  Estimated orientation BBox: [%3.3f, %3.3f, %3.3f] -> [%3.3f, %3.3f, %3.3f]\n', estim_orient_bbox);
+            fprintf('                   BBox size: %3.3f, %3.3f, %3.3f (deg)\n', bbox_size_deg);
+            fprintf('\n');
+        end
+
+        % Let's now compute the bb on the images, by computing for each corner
+        % of the space bb, the position on the detector of each corner of the
+        % orientation bb.
+        or = get_6D_space_corners(estim_space_bbox_mm, bbox_size_mm, estim_orient_bbox, bbox_size_rod, refgr, p);
+
+        refor = struct( ...
+            'id', refgr.id, 'phaseid', refgr.phaseid, 'gids', grs_list, ...
+            'center', estim_space_bbox_mm(1:3) + bbox_size_mm / 2, ...
+            'R_vector', estim_orient_bbox(1:3) + bbox_size_rod / 2 );
+        refor = gtCalculateGrain(refor, p);
+        refor = reorder_reflections(refor, refgr);
+
+        uvw_tab = get_uvw_tab(or, fwdsim_inc_refl);
+
+        refor_ws = refor.allblobs.omega(fwdsim_inc_refl) / p.labgeo.omstep;
+        uvw_tab = recenter_w_tab(uvw_tab, refor_ws, p);
+
+        img_bboxes{ii_g} = [
+            squeeze(floor(min(uvw_tab, [], 2))), ...
+            squeeze( ceil(max(uvw_tab, [], 2))) ];
+        img_sizes{ii_g} = img_bboxes{ii_g}(:, 4:6) - img_bboxes{ii_g}(:, 1:3) + 1;
+
+        refor.bb_ors = or;
+
+        samp_ors(ii_g) = refor;
+    end
+
+    % We avoid the vertical spots for convenience
+    bad_etas = false(size(fwdsim_inc_refl));
+    for ii_g = 1:num_grains
+        refor_ns = samp_ors(ii_g).allblobs.eta(fwdsim_inc_refl);
+        bad_etas = bad_etas | acosd(abs(cosd(refor_ns))) < conf.min_eta;
+        if (conf.verbose)
+            fprintf('Grain: %d\n', ii_g)
+            fprintf('%02d) du %8d, dv %8d, dw %8d, eta: %7.3f <- used: %d, u: [%4d %4d], v: [%4d %4d], w: [%4d %4d]\n', ...
+                [(1:numel(bad_etas))', img_sizes{1}, refor_ns, ~bad_etas, img_bboxes{1}(:, [1 4 2 5 3 6]) ]');
+            fprintf('\n')
+        end
+    end
+
+    for ii_g = 1:num_grains
+        img_bboxes{ii_g} = img_bboxes{ii_g}(~bad_etas, :);
+        img_sizes{ii_g} = img_sizes{ii_g}(~bad_etas, :);
+    end
+
+    [stackUSize, stackVSize] = get_stack_UV_size(cat(1, img_sizes{:}), p, conf);
+
+    inc_reflections = find(1:numel(fwdsim_inc_refl));
+    inc_reflections = inc_reflections(~bad_etas);
+
+    ref_BBus = cat(1, refgr.proj.bl(inc_reflections).mbbu);
+    ref_BBvs = cat(1, refgr.proj.bl(inc_reflections).mbbv);
+    ref_BBsizes = cat(1, refgr.proj.bl(inc_reflections).mbbsize);
+    ref_norm_BBs = [ref_BBus(:, 1), ref_BBvs(:, 1), ref_BBsizes(:, 1:2)];
+
+    ref_BBus = (ref_BBus + cat(1, refgr.proj.bl(inc_reflections).bbuim)) / 2;
+    ref_BBvs = (ref_BBvs + cat(1, refgr.proj.bl(inc_reflections).bbvim)) / 2;
+    ref_BBsizes = (ref_BBsizes + cat(1, refgr.proj.bl(inc_reflections).bbsize)) / 2;
+    ref_cut_BBs = [ref_BBus(:, 1), ref_BBvs(:, 1), ref_BBsizes(:, 1:2)];
+
+    inc_ref_ondet = ref_ondet(ref_included(inc_reflections));
+
+    inc_ref_omegas = samp_ors(1).allblobs.omega(inc_ref_ondet);
+
+    for ii_g = 1:num_grains
+        fprintf('Loading raw images for grain %d: ', ii_g)
+
+        if (ii_g > 1)
+            w_diff = inc_ref_omegas - samp_ors(ii_g).allblobs.omega(inc_ref_ondet);
+            % Could be improved
+            shared_parent = abs(w_diff) < 1;
+        end
+
+        temp_gr = grs(ii_g);
+        temp_gr.center = refgr.center;
+        proj_bls = gtFwdSimPredictProjectedGrainBBox(temp_gr, ref_norm_BBs, inc_ref_omegas, inc_ref_ondet, p);
+        proj_cut_bls = gtFwdSimPredictProjectedGrainBBox(temp_gr, ref_cut_BBs, inc_ref_omegas, inc_ref_ondet, p);
+
+        num_blobs = numel(inc_reflections);
+        blobs(1:num_blobs) = get_default_blob();
+
+        for ii_b = 1:num_blobs
+            num_chars = fprintf('%02d/%02d', ii_b, num_blobs);
+
+            if (ii_g == 1 || ~shared_parent(ii_b))
+                gbl_norm = proj_bls(ii_b);
+                gbl_cut = proj_cut_bls(ii_b);
+                blobs(ii_b) = load_blob(img_bboxes{ii_g}(ii_b, :), ...
+                    img_sizes{ii_g}(ii_b, :), stackUSize, stackVSize, gbl_norm, gbl_cut, p); %#ok<AGROW>
+            else
+                % Using the same because it gives the same size/uvw-limits
+                blobs(ii_b) = samp_ors(1).proj.bl(ii_b); %#ok<AGROW>
+            end
+
+            fprintf(repmat('\b', [1 num_chars]));
+            fprintf(repmat(' ', [1 num_chars]));
+            fprintf(repmat('\b', [1 num_chars]));
+        end
+        fprintf('Done.\n')
+
+        spots = arrayfun(@(x){sum(x.intm, 3)}, blobs);
+        spots = permute(cat(3, spots{:}), [1 3 2]);
+
+        proj.centerpix = estim_space_bbox_pix(1:3) + bbox_size_pix / 2;
+        proj.bl = blobs;
+        proj.stack = spots;
+        vol_size = bbox_size_pix + mean(bbox_size_pix) * 0.3;
+        proj.vol_size_x = vol_size(2);
+        proj.vol_size_y = vol_size(1);
+        proj.vol_size_z = vol_size(3);
+
+        proj.ondet = ref_ondet;
+        proj.included = ref_included(inc_reflections);
+        proj.selected = true(size(proj.included));
+
+        if (ii_g > 1)
+            proj.selected = samp_ors(1).proj.selected;
+            proj.shared_parent = shared_parent; % <- size: proj.included
+        else
+            proj.selected = true(size(proj.included));
+        end
+
+        samp_ors(ii_g).proj = proj;
+
+        if (conf.check_twin_spots)
+            samp_ors(ii_g).proj.selected = samp_ors(1).proj.selected & gtGuiGrainMontage(samp_ors(ii_g), [], true);
+            for ii_g_o = 1:ii_g-1
+                samp_ors(ii_g_o).proj.selected = samp_ors(ii_g_o).proj.selected & samp_ors(ii_g).proj.selected;
+            end
+        end
+    end
+
+    partial_ints = zeros(numel(inc_reflections), num_grains);
+    phys_renorm = ones(numel(inc_reflections), num_grains);
+
+    partial_ints(:, 1) = cat(1, samp_ors(1).proj.bl.intensity);
+    for ii_g = 2:num_grains
+        to_be_summed = ~samp_ors(ii_g).proj.shared_parent;
+        partial_ints(to_be_summed, ii_g) = cat(1, samp_ors(ii_g).proj.bl(to_be_summed).intensity);
+    end
+
+    if (~conf.flat_normalization)
+        for ii_g = 1:num_grains
+            try
+                temp_or = gtComputeIncomingBeamIntensity(samp_ors(ii_g), p);
+                beam_ints = temp_or.beam_intensity;
+            catch mexc
+                gtPrintException(mexc, 'Turning beam inensity correction off, because interlaced acquisition is not supported')
+                beam_ints = 1;
+            end
+            phys_renorm(:, ii_g) = 1 ...
+                ./ samp_ors(ii_g).allblobs.lorentzfac(inc_ref_ondet) ...
+                ./ (beam_ints / mean(beam_ints));
+        end
+    end
+    total_ints = sum(partial_ints .* phys_renorm, 2);
+
+    % Let's renormalize
+    for ii_g = 1:num_grains
+        for ii_b = 1:num_blobs
+            ints_renorm = phys_renorm(:, ii_g) ./ total_ints(:);
+
+            if (ii_g > 1 && samp_ors(ii_g).proj.shared_parent(ii_b))
+                fprintf('Shared!\n')
+            else
+                samp_ors(ii_g).proj.bl(ii_b).intm = samp_ors(ii_g).proj.bl(ii_b).intm * ints_renorm(ii_b);
+            end
+        end
+    end
+
+    if (conf.verbose)
+        f = figure();
+        ax = axes('parent', f);
+        hold(ax, 'on');
+        for ii_g = 1:num_grains
+            gt6DPlotOrientationBBox(ax, cat(1, samp_ors(ii_g).bb_ors(:).R_vector));
+            scatter3(ax, grs(ii_g).R_vector(1), grs(ii_g).R_vector(2), grs(ii_g).R_vector(3), 30);
+        end
+        hold(ax, 'off');
+
+        drawnow();
+    end
+
+    if (conf.save)
+        str_ids = sprintf('_%04d', grs_list);
+        grain_filename = fullfile(p.acq.dir, '4_grains', ...
+            sprintf('phase_%02d', phase_id), ...
+            sprintf('grain_twin_cluster%s.mat', str_ids));
+        fprintf('Saving the cluster file "%s"..', grain_filename)
+        save(grain_filename, 'samp_ors', '-v7.3');
+        fprintf('\b\b: Done.\n')
+
+%         fprintf('Saving to sample.mat..')
+%         sample = GtSample.loadFromFile();
+%         sample.phases{phase_id}.clusters(end+1) = ...
+%             GtPhase.makeCluster(grs_list, refor.R_vector, estim_space_bbox_pix, estim_orient_bbox);
+%         sample.saveToFile();
+%         fprintf('\b\b: Done.\n')
+    end
+end
+
+function orients = get_6D_space_corners(estim_space_bbox_mm, bbox_size_mm, estim_orient_bbox, bbox_size_rod, refgr, p)
+    orients = {};
+    for ii_x1 = 0:1
+        base_x1 = estim_space_bbox_mm(1:3) + [ii_x1 * bbox_size_mm(1), 0, 0];
+        for ii_x2 = 0:1
+            base_x2 = base_x1 + [0, ii_x2 * bbox_size_mm(2), 0];
+            for ii_x3 = 0:1
+                base_x3 = base_x2 + [0, 0, ii_x3 * bbox_size_mm(3)];
+                for ii_r1 = 0:1
+                    base_r1 = estim_orient_bbox(1:3) + [ii_r1 * bbox_size_rod(1), 0, 0];
+                    for ii_r2 = 0:1
+                        base_r2 = base_r1 + [0, ii_r2 * bbox_size_rod(2), 0];
+                        for ii_r3 = 0:1
+                            base_r3 = base_r2 + [0, 0, ii_r3 * bbox_size_rod(3)];
+
+%                             fprintf(' center: %f, %f, %f, r_vec: %f, %f, %f\n', base_x3, base_r3)
+
+                            orients{end+1} = struct( ...
+                                'id', refgr.id, 'phaseid', refgr.phaseid, ...
+                                'center', base_x3, 'R_vector', base_r3); %#ok<AGROW>
+                        end
+                    end
+                end
+            end
+        end
+    end
+    orients = gtCalculateGrain_p(orients, p);
+    orients = [orients{:}];
+
+    orients = reorder_reflections(orients, refgr);
+end
+
+function orients = reorder_reflections(orients, refgr)
+    refgr_omind = refgr.allblobs.omind;
+    refgr_omind_ind = [ ...
+        find(refgr_omind == 1); find(refgr_omind == 2); ...
+        find(refgr_omind == 3); find(refgr_omind == 4) ];
+
+    for ii_g = numel(orients):-1:1
+        orients(ii_g) = gtGrainAllblobsFilterOrder(orients(ii_g), refgr_omind_ind);
+    end
+end
+
+function uvw_tab = recenter_w_tab(uvw_tab, refor_ws, p)
+    num_images = gtGetTotNumberOfImages(p);
+
+    % Let's treat those blobs at the w edge 360->0
+    % (from the sampled orientations perspective)
+    opposite_ws = mod(refor_ws + num_images / 2, num_images);
+    gt_ref_int = opposite_ws > num_images / 2;
+    lt_ref_int = ~gt_ref_int;
+    opposite_ws_repmat = opposite_ws(:, ones(1, size(uvw_tab(:, :, 3), 2)));
+
+    uvw_tab(gt_ref_int, :, 3) = uvw_tab(gt_ref_int, :, 3) ...
+        - num_images .* (uvw_tab(gt_ref_int, :, 3) > opposite_ws_repmat(gt_ref_int, :));
+    uvw_tab(lt_ref_int, :, 3) = uvw_tab(lt_ref_int, :, 3) ...
+        + num_images .* (uvw_tab(lt_ref_int, :, 3) < opposite_ws_repmat(lt_ref_int, :));
+end
+
+function uvw_tab = get_uvw_tab(orientations, refl_sel)
+    num_ors = numel(orientations);
+    or_abs = cat(1, orientations(:).allblobs);
+    uvw_tab = zeros(numel(refl_sel), num_ors, 3);
+    for ii_o = 1:num_ors
+        if (isfield(or_abs(1), 'detector'))
+            uvw_tab(:, ii_o, :) = or_abs(ii_o).detector(1).uvw(refl_sel, :);
+        else
+            uvw_tab(:, ii_o, :) = or_abs(ii_o).uvw(refl_sel, :);
+        end
+    end
+end
+
+function [u_size, v_size] = get_stack_UV_size(img_sizes, p, conf)
+
+    max_img_sizes = [max(img_sizes(:, 1)), max(img_sizes(:, 2))];
+    stack_imgs_oversize = min(p.fsim.oversize, conf.oversize);
+    u_size = round(max_img_sizes(1) * stack_imgs_oversize);
+    v_size = round(max_img_sizes(2) * stack_imgs_oversize);
+
+    if (conf.verbose)
+        fprintf('\n');
+        fprintf('               Maximum images size: [%3d, %3d]\n', max_img_sizes);
+        fprintf('Stack images size (oversize: %1.2f): [%3d, %3d]\n', stack_imgs_oversize, u_size, v_size);
+        fprintf('\n');
+    end
+end
+
+function blob = load_blob(img_bboxes, img_sizes, stackUSize, stackVSize, gbl_norm, gbl_cut, p)
+    blob = get_default_blob();
+
+    bb = [img_bboxes(1, 1:2), img_sizes(1, 1:2)];
+    blob_vol = gtGetRawRoi(img_bboxes(3), img_bboxes(6), p.acq, bb);
+    blob_vol(blob_vol < 0) = 0;
+    blob_vol(isnan(blob_vol)) = 0;
+    blob_bb = [img_bboxes(1, 1:3), img_sizes(1, :)];
+
+    % Transposing to keep the same convention as spots
+    blob_vol = permute(blob_vol, [2 1 3]);
+
+    blob.mbbsize = blob_bb(4:6);
+    blob.mbbu = [blob_bb(1), blob_bb(1) + blob_bb(4) + 1];
+    blob.mbbv = [blob_bb(2), blob_bb(2) + blob_bb(5) + 1];
+    blob.mbbw = [blob_bb(3), blob_bb(3) + blob_bb(6) + 1];
+
+    blob_size_im = [stackUSize, stackVSize, blob_bb(6)+2];
+
+    shifts_blob = gtFwdSimGetStackShifts(stackUSize, stackVSize, blob_bb, false);
+    shifts_blob = [shifts_blob.u, shifts_blob.v, 1];
+
+    blob.intm = gtPlaceSubVolume( ...
+        zeros(blob_size_im, 'single'), single(blob_vol), shifts_blob);
+
+    blob.bbsize = blob_size_im;
+
+    blob_bb_im = [blob_bb(1, 1:3) - shifts_blob, blob_size_im];
+
+    blob.bbuim = [blob_bb_im(1), blob_bb_im(1) + blob_bb_im(4) - 1];
+    blob.bbvim = [blob_bb_im(2), blob_bb_im(2) + blob_bb_im(5) - 1];
+    blob.bbwim = [blob_bb_im(3), blob_bb_im(3) + blob_bb_im(6) - 1];
+
+    % We should build the mask from the segmented reflections of the
+    % grains
+    blob.mask = false(blob_size_im);
+
+    % spreading the flat mask over the full expected BBox in UVW
+    grain_mask = gbl_cut.mask(:, :, ones(size(blob.mask, 3)));
+
+    gbl_shifts_im = [gbl_cut.bbuim(1), gbl_cut.bbvim(1), blob.bbwim(1)]; % <- note w component: blob.bbwim(1)
+    blob_shifts_im = [blob.bbuim(1), blob.bbvim(1), blob.bbwim(1)];
+    mask_shifts = gbl_shifts_im - blob_shifts_im;
+
+    blob.mask = gtPlaceSubVolume(blob.mask, grain_mask, mask_shifts);
+    blob.intm(~blob.mask) = 0;
+
+    % We should build the mask from the segmented reflections of the
+    % grains
+    % for the moment we only use one (the reference one), to
+    % renormalize the different reflections
+    blob.mask = false(blob_size_im);
+
+    % spreading the flat mask over the full expected BBox in UVW
+    grain_mask = gbl_norm.mask(:, :, ones(size(blob.mask, 3)));
+
+    gbl_shifts_im = [gbl_norm.bbuim(1), gbl_norm.bbvim(1), blob.bbwim(1)]; % <- note w component: blob.bbwim(1)
+    blob_shifts_im = [blob.bbuim(1), blob.bbvim(1), blob.bbwim(1)];
+    mask_shifts = gbl_shifts_im - blob_shifts_im;
+
+    blob.mask = gtPlaceSubVolume(blob.mask, grain_mask, mask_shifts);
+
+    blob_int = sum(blob.intm(blob.mask));
+%     blob.intm = blob.intm / blob_int;
+
+    blob.intensity = blob_int;
+end
+
+function blob = get_default_blob()
+    blob = struct( ...
+        'intm', [], ...         % The normalized blob
+        'mask', [], ...         % The segmentation mask
+        'bbuim', [], ...        % Image BB on U
+        'bbvim', [], ...        % Image BB on V
+        'bbwim', [], ...        % Image BB on W <- Not strictly w
+        'bbsize', [], ...       % Image BoundingBox (includes margins)
+        'mbbsize', [], ...      % Real (Measured) Blob Bounding Box
+        'mbbu', [], ...         % Real (Measured) Blob BB on U
+        'mbbv', [], ...         % Real (Measured) Blob BB on V
+        'mbbw', [], ...         % Real (Measured) Blob BB on W <- Not strictly w
+        'intensity', [] );      % Blob original intensity
+end
+
+
+
+
+
diff --git a/zUtil_Deformation/test_generation/do6D.m b/zUtil_Deformation/test_generation/do6D.m
index 8cc63fee..f279642d 100755
--- a/zUtil_Deformation/test_generation/do6D.m
+++ b/zUtil_Deformation/test_generation/do6D.m
@@ -186,7 +186,7 @@ switch(mode)
         recon_algo_factory = Gt6DReconstructionAlgorithmFactory();
 %         [reconstructor, blobs] = recon_algo_factory.getBlobReconstructionAlgo( ...
 %             test_data.bl, blobs_on_det, sampler, '2D');
-        [reconstructor, blobs] = recon_algo_factory.getSubBlobReconstructionAlgo( ...
+        [reconstructor, blobs] = recon_algo_factory.getReconstructionAlgo( ...
             test_data.bl, blobs_on_det, sampler, '2D', num_interp);
     case 'testPeterSubBlob3D'
         cd('~/data/testdata/')
@@ -230,7 +230,7 @@ switch(mode)
         end
 
         recon_algo_factory = Gt6DReconstructionAlgorithmFactory();
-        [reconstructor, blobs] = recon_algo_factory.getSubBlobReconstructionAlgo( ...
+        [reconstructor, blobs] = recon_algo_factory.getReconstructionAlgo( ...
             test_data.bl, blobs_on_det, sampler, '3D', num_interp);
     case 'peterDataset'
         cd('/mntdirect/_data_id19_degisher/mi1026/id18/AlLi2b_vert_ ')
-- 
GitLab