diff --git a/control/descriptor_helpers.c b/control/descriptor_helpers.c
index dd5c3981d257af213bcdf54f47604a9d153449f9..f11e487d32d578577534de023b37179765e263ff 100644
--- a/control/descriptor_helpers.c
+++ b/control/descriptor_helpers.c
@@ -11,14 +11,15 @@
  *
  * @brief Chameleon descriptors routines
  *
- * @version 1.2.0
+ * @version 1.3.0
  * @author Mathieu Faverge
  * @author Cedric Castagnede
  * @author Florent Pruvost
  * @author Guillaume Sylvand
  * @author Raphael Boucherie
  * @author Samuel Thibault
- * @date 2022-02-22
+ * @author Lionel Eyraud-Dubois
+ * @date 2023-07-05
  *
  ***
  *
@@ -81,6 +82,149 @@ int chameleon_getrankof_2d_diag( const CHAM_desc_t *A, int m, int n )
     return (mm % A->p) * A->q + (mm % A->q);
 }
 
+/**
+ * @brief Initializes a custom distribution based on an external file.
+ *
+ *  External file format: First line contains the dimensions M and N (space-separated)
+ *  Next M lines each have N integers (with values from 1 to number of nodes)
+ *
+ *
+ * @param[out] custom_dist
+ *        On exit, the resulting custom distribution
+ *
+ * @param[in] dist_file
+ *        The path to the external file to be read
+ *
+ * @return CHAMELEON_SUCCESS on successful exit, CHAMELEON_ERR_OUT_OF_RESOURCES
+ *         or CHAMELEON_ERR_ILLEGAL_VALUE on issue.
+ *
+ */
+int chameleon_getrankof_custom_init( custom_dist_t **custom_dist,
+                                     const char     *dist_file )
+{
+    custom_dist_t *result;
+    FILE          *f;
+    int            i, j, rc;
+    int            np, dist_m, dist_n;
+
+    *custom_dist = NULL;
+
+    /* Get number of processes to check for correctness */
+    np = CHAMELEON_Comm_size();
+
+    /* Allocate memory */
+    result = (custom_dist_t*) malloc( sizeof(custom_dist_t) );
+    if ( result == NULL ) {
+        chameleon_error( "chameleon_getrankof_custom_init", "malloc() failed" );
+        return CHAMELEON_ERR_OUT_OF_RESOURCES;
+    }
+
+    result->dist_file = dist_file;
+    f = fopen( result->dist_file , "r" );
+    if ( f == NULL ) {
+        char message[300];
+        snprintf( message, 300, "could not open file '%s'", dist_file );
+        chameleon_error( "chameleon_getrankof_custom_init", message );
+        free( result );
+        return CHAMELEON_ERR_ILLEGAL_VALUE;
+    }
+
+    rc = fscanf( f, "%d %d", &dist_m, &dist_n );
+    if ( rc < 2 ) {
+        char message[300];
+        snprintf( message, 300, "could not read m and n in file '%s'", dist_file );
+        chameleon_error( "chameleon_getrankof_custom_init", message );
+        free( result );
+        fclose( f );
+        return CHAMELEON_ERR_ILLEGAL_VALUE;
+    }
+
+    result->dist_m = dist_m;
+    result->dist_n = dist_n;
+
+    result->blocks_dist = (int*) malloc( sizeof(int) * dist_m * dist_n );
+    if ( result->blocks_dist == NULL ) {
+        chameleon_error( "chameleon_getrankof_custom_init", "could not allocate blocks table" );
+        free( result );
+        fclose( f );
+        return CHAMELEON_ERR_OUT_OF_RESOURCES;
+    }
+
+    for(i = 0; i < dist_m; i++) {
+        for(j = 0; j < dist_n; j++) {
+            int rank;
+
+            rc = fscanf( f, "%d", &rank );
+            if ( rc < 1 ) {
+                char message[300];
+                snprintf(message, 300, "file '%s': could not read value at position (%d, %d)", dist_file, i, j );
+                chameleon_error( "chameleon_getrankof_custom_init", message );
+                free( result->blocks_dist );
+                free( result );
+                fclose( f );
+                return CHAMELEON_ERR_ILLEGAL_VALUE;
+            }
+
+            if ( (rank < 0 ) || (rank >= np) )
+            {
+                char message[300];
+                snprintf( message, 300, "file '%s': value %d at position (%d, %d) is invalid with %d processes",
+                          dist_file, rank, i, j, np );
+                chameleon_error( "chameleon_getrankof_custom_init", message );
+                free( result->blocks_dist );
+                free( result );
+                fclose( f );
+                return CHAMELEON_ERR_ILLEGAL_VALUE;
+            }
+
+            result->blocks_dist[j * dist_m + i] = rank;
+        }
+    }
+    fclose(f);
+
+    *custom_dist = result;
+    return CHAMELEON_SUCCESS;
+}
+
+/**
+ * @brief Destroys a custom distribution based on an external file.
+ *
+ * @param[int] dist
+ *        The custom distribution to be destroyed
+ *
+ * @return CHAMELEON_SUCCESS on successful exit, CHAMELEON_ERR_UNALLOCATED otherwise.
+ *
+ */
+int
+chameleon_getrankof_custom_destroy( custom_dist_t **dist )
+{
+    if ((dist == NULL) || (*dist == NULL)) {
+        chameleon_error("chameleon_getrankof_custom_destroy", "attempting to destroy a NULL descriptor");
+        return CHAMELEON_ERR_UNALLOCATED;
+    }
+
+    free((*dist)->blocks_dist);
+    free(*dist);
+    *dist = NULL;
+    return CHAMELEON_SUCCESS;
+}
+
+/**
+ * @brief Internal function to return MPI rank of block (m,n) in distribution
+ * custom from dist file
+ *
+ * @param[in] desc matrix
+ * @param[in] m row index of tile to consider
+ * @param[in] n column index of tile to consider
+ *
+ * @return The rank of the tile at coordinate (m, n).
+ */
+int chameleon_getrankof_custom( const CHAM_desc_t *desc, int m, int n )
+{
+    custom_dist_t *dist = desc->get_rankof_init_arg;
+    return dist->blocks_dist[(n % dist->dist_n) * dist->dist_m + (m % dist->dist_m)];
+}
+
 /**
  * @brief Return the address of the tile A( m, n ) in a tile storage.
  *
diff --git a/include/chameleon/descriptor_helpers.h b/include/chameleon/descriptor_helpers.h
index c732e930c8e05824da8ebf675bc38126f5a21ea7..185b161778526714c3c07c1367a17c0731661ea5 100644
--- a/include/chameleon/descriptor_helpers.h
+++ b/include/chameleon/descriptor_helpers.h
@@ -11,7 +11,7 @@
  *
  * @brief Set of functions to help the user to declare matrix descriptors (allocation, mapping... )
  *
- * @version 1.2.0
+ * @version 1.3.0
  * @author Jakub Kurzak
  * @author Mathieu Faverge
  * @author Cedric Castagnede
@@ -19,7 +19,8 @@
  * @author Guillaume Sylvand
  * @author Raphael Boucherie
  * @author Samuel Thibault
- * @date 2020-03-03
+ * @author Lionel Eyraud-Dubois
+ * @date 2023-07-05
  *
  * @addtogroup chameleon_descriptors
  * @{
@@ -43,8 +44,18 @@ extern "C" {
  * @name Mapping functions
  * @{
  */
-int chameleon_getrankof_2d      ( const CHAM_desc_t *A, int m, int n );
-int chameleon_getrankof_2d_diag ( const CHAM_desc_t *A, int m, int n );
+int chameleon_getrankof_2d     ( const CHAM_desc_t *A, int m, int n );
+int chameleon_getrankof_2d_diag( const CHAM_desc_t *A, int m, int n );
+
+typedef struct custom_dist_s{
+    int *blocks_dist;         // Matrix of size dist_m times dist_n with values from 1 to number of process MPI
+    int dist_m, dist_n;       // The matrix has dist_m rows of dist_n elements
+    const char* dist_file;    // Name of the file that contains the distribution
+} custom_dist_t;
+
+int chameleon_getrankof_custom_init   ( custom_dist_t **dist, const char *filename );
+int chameleon_getrankof_custom_destroy( custom_dist_t **dist );
+int chameleon_getrankof_custom        ( const CHAM_desc_t *A, int m, int n );
 
 /**
  * @}