diff --git a/5_reconstruction/gtReconstructGrainTwinClusters.m b/5_reconstruction/gtReconstructGrainTwinClusters.m
new file mode 100644
index 0000000000000000000000000000000000000000..b200c67edb73aaf3ab197411c352b69d1eea1bd5
--- /dev/null
+++ b/5_reconstruction/gtReconstructGrainTwinClusters.m
@@ -0,0 +1,185 @@
+function gtReconstructGrainTwinClusters(first, last, workingdirectory, phase_id, parameters, varargin)
+% gtReconstructGrainOrientation  6D reconstructions on a GPU machine
+%     gtAstraReconstructGrainTwinClusters(first, last, workingdirectory, phaseID, [parameters], varargin)
+%     -------------------------------------------------------
+    cd(workingdirectory);
+
+    if (~exist('parameters', 'var') || isempty(parameters))
+        parameters = gtLoadParameters();
+    end
+
+    parameters.fsim.check_spot = false;
+
+    conf = struct( ...
+        'recompute_osbb', true, ...
+        'ospace_resolution', [], ...
+        'ospace_lims', [], ...
+        'list', []);
+    
+    [conf, ~] = parse_pv_pairs(conf, varargin);
+    
+    if (isdeployed)
+        global GT_DB %#ok<NUSED,TLEV>
+        global GT_MATLAB_HOME %#ok<NUSED,TLEV>
+        load('workspaceGlobal.mat');
+        phase_id   = str2double(phase_id);
+        first = str2double(first);
+        last  = str2double(last);
+    end
+ 
+    sample = GtSample.loadFromFile;
+    
+    if isempty(conf.list)
+        cluster_list = first : last;
+    else
+        cluster_list = conf.list;
+        % in case the code is deployed, we have to convert to strings
+        if ischar(cluster_list)
+            cluster_list = sscanf(cluster_list, '%d');
+        end
+    end
+    
+    num_clusters = numel(cluster_list);
+    
+    for ii_cl = 1 : num_clusters
+        cluster_id = cluster_list(ii_cl);
+        grain_ids = sample.phases{phase_id}.clusters(cluster_id).included_ids;
+
+        rec_opts = gtReconstruct6DGetParamenters(parameters);
+        if (isfield(parameters.rec, 'grains') ...
+                && isfield(parameters.rec.grains, 'options') ...
+                && ~isempty(parameters.rec.grains.options) ...
+                && (~isfield(parameters.rec.grains.options, 'use_predicted_scatter_ints') ...
+                    || isempty(parameters.rec.grains.options.use_predicted_scatter_ints)) )
+            rec_opts.use_predicted_scatter_ints = true;
+        end
+
+        phase_dir = fullfile(parameters.acq.dir, '4_grains', ...
+            sprintf('phase_%02d', phase_id));
+        if (isstruct(grain_ids))
+            grs = grain_ids.samp_ors;
+            grains_str_ids = sprintf('_%04d', cat(2, grs.id));
+        else
+            grains_str_ids = sprintf('_%04d', grain_ids);
+            grain_cluster_file = fullfile(phase_dir, ...
+                sprintf('grain_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')
+        end
+        num_grains = numel(grs);
+
+        for ii_g = 1:num_grains
+            sampler(ii_g) = GtOrientationSampling(parameters, grs(ii_g)); %#ok<AGROW>
+
+            if (~isempty(conf.ospace_resolution))
+                if (numel(conf.ospace_resolution) == 1)
+                    conf.ospace_resolution = conf.ospace_resolution(ones(num_grains, 1));
+                end
+                if (isempty(conf.ospace_lims) || (size(conf.ospace_lims, 1) < ii_g))
+                    bb_gvdm = cat(1, grs(ii_g).bb_ors(:).R_vector);
+                else
+                    diff_r_vecs = tand(conf.ospace_lims(ii_g, :) / 2);
+                    bb_gvdm = [grs(ii_g).R_vector + diff_r_vecs(1:3); grs(ii_g).R_vector + diff_r_vecs(4:6)];
+                end
+                sampler(ii_g).make_res_simple_grid('cubic', conf.ospace_resolution(ii_g), bb_gvdm', rec_opts.ospace_oversize);
+            elseif (conf.recompute_osbb)
+                sampler(ii_g).make_simple_grid_estim_ODF('cubic', rec_opts.grid_edge, false, rec_opts.ospace_oversize);
+            else
+                bb_gvdm = cat(1, grs(ii_g).bb_ors(:).R_vector);
+                sampler(ii_g).make_even_simple_grid('cubic', rec_opts.grid_edge, bb_gvdm', rec_opts.ospace_oversize)
+            end
+            if (rec_opts.ospace_super_sampling > 1)
+                sampler(ii_g).make_supersampling_simple_grid([1 2 3], rec_opts.ospace_super_sampling);
+            end
+        end
+
+        algo = gtReconstruct6DLaunchAlgorithm(sampler, rec_opts, parameters);
+
+        vols = algo.getCurrentSolution();
+
+        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));
+
+            [avg_R_vecs, avg_R_vecs_int, stddev_R_vecs] = sampler(ii_g).getAverageOrientations(gr_vols);
+            avg_R_vec = sampler(ii_g).getAverageOrientation(gr_vols);
+            s_g_odf = reshape(sampler(ii_g).getODF(gr_vols), or_sizes{1});
+
+            [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);
+
+            % Restoring initial volume size (depending on the rounding)
+            if (rec_opts.volume_downscaling > 1)
+                avg_R_vecs_int = gtMathsUpsampleVolume(avg_R_vecs_int, rec_opts.volume_downscaling);
+                avg_R_vecs = gtMathsUpsampleVolume(avg_R_vecs, rec_opts.volume_downscaling);
+                stddev_R_vecs = gtMathsUpsampleVolume(stddev_R_vecs, rec_opts.volume_downscaling);
+                kam = gtMathsUpsampleVolume(kam, rec_opts.volume_downscaling);
+                igm = gtMathsUpsampleVolume(igm, rec_opts.volume_downscaling);
+            end
+
+            vol_size = size(avg_R_vecs_int);
+            shift = gtFwdSimComputeVolumeShifts(grs(ii_g).proj, parameters, vol_size);
+
+            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()}, ...
+                'voxels_stddev_R_vectors', {stddev_R_vecs}, ...
+                '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_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();
+
+            % 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
+
+            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_cluster_full_details%s.mat', grains_str_ids));
+            save(grain_full_details_file, 'ODF6D', '-v7.3');
+        end
+    end
+end