diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2f85fb2afeeeacd128a34564d7ac71f89eb51fd6..b710baa73443ee96cac86b7532869b63d5a5bb4a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,13 +4,21 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## Next version
+
+[met4j-io] Fix SetIdsFromFile : do not throw an exception when a new id is found twice
+
+
 ## 1.2.0
 
 ### Features
 
 [met4j-toolbox] Add Apps to compute compound graph's classical weights (degree/chemical similarity)
+
 [met4j-toolbox] Add App to identify model seeds and targets
+
 [met4j-toolbox] Add App to set new ids to metabolic entities in a SBML file
+
 [met4j-graph] Add method to create RPAIRs-like tags on compound graph's edges
 
 ## 1.1.1
diff --git a/coverage/pom.xml b/coverage/pom.xml
index 4dd7d5c3900843c83db76af57d68a870778e6e09..58bd7b29f9d5c764aeea078e5be263c140e34c26 100644
--- a/coverage/pom.xml
+++ b/coverage/pom.xml
@@ -80,6 +80,12 @@
             <artifactId>met4j-mapping</artifactId>
             <version>${project.version}</version>
         </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>met4j-reconstruction</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/met4j-chemUtils/pom.xml b/met4j-chemUtils/pom.xml
index 62212bfbfd1f42cb9401dc9eab2344a3a2a2aaf1..d025c5174b52b414427e389082563daea2cbfa9c 100644
--- a/met4j-chemUtils/pom.xml
+++ b/met4j-chemUtils/pom.xml
@@ -21,12 +21,6 @@
     <url>http://maven.apache.org</url>
 
     <dependencies>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <version>4.12</version>
-            <scope>test</scope>
-        </dependency>
         <dependency>
             <groupId>org.openscience.cdk</groupId>
             <artifactId>cdk-bundle</artifactId>
diff --git a/met4j-core/pom.xml b/met4j-core/pom.xml
index ede3e822d54a464c1ba3c0cf082a1b6e8243c7cc..0be4d4d55384d06f5eecfab86450dfeaf78c6417 100644
--- a/met4j-core/pom.xml
+++ b/met4j-core/pom.xml
@@ -27,12 +27,7 @@
     </properties>
 
     <dependencies>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <version>4.12</version>
-            <scope>test</scope>
-        </dependency>
+
         <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/utils/StringUtils.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/utils/StringUtils.java
index 9810059fb7ddc9f81c38dee9583055409565ca3d..9976c2cddadd398e98a116084a092b75b2de9b8d 100644
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/utils/StringUtils.java
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/utils/StringUtils.java
@@ -126,7 +126,11 @@ public class StringUtils {
 	 * @param formula a String to check
 	 * @return true if it looks like a chemical formula (e.g. CH3, C, (n)CH2O6)
 	 */
-	public static boolean checkMetaboliteFormula(@NonNull String formula) {
+	public static boolean checkMetaboliteFormula(String formula) {
+
+		if(formula == null) {
+			return false;
+		}
 		Matcher m = patternFormula.matcher(formula);
 		return m.matches();
 	}
diff --git a/met4j-graph/pom.xml b/met4j-graph/pom.xml
index 8d8662a7c000c53562748538f76929e2294acf64..8ff197c446aee69f0b0e340a278b84b1bd843d6b 100644
--- a/met4j-graph/pom.xml
+++ b/met4j-graph/pom.xml
@@ -21,12 +21,6 @@
     </properties>
 
     <dependencies>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <version>4.12</version>
-            <scope>test</scope>
-        </dependency>
         <dependency>
             <groupId>org.jgrapht</groupId>
             <artifactId>jgrapht-core</artifactId>
diff --git a/met4j-io/pom.xml b/met4j-io/pom.xml
index 9869a361f3508b8fbb5e1d1fb3ee10a1c76b6d67..a5aea77312ff2da49010fdf48e0224c258cea025 100644
--- a/met4j-io/pom.xml
+++ b/met4j-io/pom.xml
@@ -32,11 +32,6 @@
             <artifactId>jsbml</artifactId>
             <version>1.6.1</version>
         </dependency>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <version>4.12</version>
-        </dependency>
         <dependency>
             <groupId>xerces</groupId>
             <artifactId>xercesImpl</artifactId>
diff --git a/met4j-io/src/main/java/fr/inrae/toulouse/metexplore/met4j_io/tabulated/attributes/SetIdsFromFile.java b/met4j-io/src/main/java/fr/inrae/toulouse/metexplore/met4j_io/tabulated/attributes/SetIdsFromFile.java
index f32a689cb3acc3bf802ac1bbca99a3cab307752a..78a46d59836e3ddf1f560b8bed02ed1102f4befa 100644
--- a/met4j-io/src/main/java/fr/inrae/toulouse/metexplore/met4j_io/tabulated/attributes/SetIdsFromFile.java
+++ b/met4j-io/src/main/java/fr/inrae/toulouse/metexplore/met4j_io/tabulated/attributes/SetIdsFromFile.java
@@ -46,15 +46,15 @@ public class SetIdsFromFile extends AbstractSetAttributesFromFile {
     /**
      * <p>Constructor for AbstractSetAttributesFromFile.</p>
      *
-     * @param colId      a int.
+     * @param colId      an int.
      * @param colAttr    : number of the attribute column
      * @param bn         : {@link BioNetwork}
      * @param fileIn     : tabulated file containing the ids and the attributes
      * @param c          : comment character
-     * @param nSkip      a int. Number of lines to skip
+     * @param nSkip      an int. Number of lines to skip
      * @param entityType a {@link EntityType}
      * @param p          a {@link Boolean} object : To match the objects in the sbml file, adds the prefix R_ to reactions and M_ to metabolites
-     * @param s          a {@link Boolean} object : To match the objects in the sbml file, adds the suffix _comparmentID to metabolite
+     * @param s          a {@link Boolean} object : To match the objects in the sbml file, adds the suffix _compartmentID to metabolite
      */
     public SetIdsFromFile(int colId, int colAttr, BioNetwork bn, String fileIn, String c, int nSkip, EntityType entityType, Boolean p, Boolean s) {
         super(colId, colAttr, bn, fileIn, c, nSkip, entityType, p, s);
@@ -111,7 +111,7 @@ public class SetIdsFromFile extends AbstractSetAttributesFromFile {
                         break;
                     }
                     default: {
-                        throw new EntityTypeException("Entity type "+this.entityType+" not recognized");
+                        throw new EntityTypeException("Entity type " + this.entityType + " not recognized");
                     }
                 }
             }
@@ -119,7 +119,7 @@ public class SetIdsFromFile extends AbstractSetAttributesFromFile {
 
         System.err.println(n + " entities processed");
 
-        return flag;
+        return true;
     }
 
     /**
@@ -132,35 +132,38 @@ public class SetIdsFromFile extends AbstractSetAttributesFromFile {
         BioCompartment compartment = this.bn.getCompartment(id);
 
         if (compartment != null) {
-            BioCompartment newCompartment = new BioCompartment(compartment, newId);
-            this.bn.add(newCompartment);
+            BioCompartment newCompartment;
+
+            if (!this.bn.containsCompartment(newId)) {
+                newCompartment = new BioCompartment(compartment, newId);
+                this.bn.add(newCompartment);
+            } else {
+                System.err.println("[WARNING] The compartment "+newId+" already exists");
+                newCompartment = this.bn.getCompartment(newId);
+            }
 
             // Add metabolites
             this.bn.affectToCompartment(newCompartment, compartment.getComponentsView());
 
             // Change compartment in each left reactant
-            this.bn.getReactionsView().forEach(r -> {
-                r.getLeftReactantsView().forEach(reactant -> {
-                    if (reactant.getLocation().equals(compartment)) {
-                        BioMetabolite metabolite = reactant.getMetabolite();
-                        Double sto = reactant.getQuantity();
-                        this.bn.affectLeft(r, sto, newCompartment, metabolite);
-                        this.bn.removeLeft(metabolite, compartment, r);
-                    }
-                });
-            });
+            this.bn.getReactionsView().forEach(r -> r.getLeftReactantsView().forEach(reactant -> {
+                if (reactant.getLocation().equals(compartment)) {
+                    BioMetabolite metabolite = reactant.getMetabolite();
+                    Double sto = reactant.getQuantity();
+                    this.bn.affectLeft(r, sto, newCompartment, metabolite);
+                    this.bn.removeLeft(metabolite, compartment, r);
+                }
+            }));
 
             // Change compartment in each right reactant
-            this.bn.getReactionsView().forEach(r -> {
-                r.getRightReactantsView().forEach(reactant -> {
-                    if (reactant.getLocation().equals(compartment)) {
-                        BioMetabolite metabolite = reactant.getMetabolite();
-                        Double sto = reactant.getQuantity();
-                        this.bn.affectRight(r, sto, newCompartment, metabolite);
-                        this.bn.removeRight(metabolite, compartment, r);
-                    }
-                });
-            });
+            this.bn.getReactionsView().forEach(r -> r.getRightReactantsView().forEach(reactant -> {
+                if (reactant.getLocation().equals(compartment)) {
+                    BioMetabolite metabolite = reactant.getMetabolite();
+                    Double sto = reactant.getQuantity();
+                    this.bn.affectRight(r, sto, newCompartment, metabolite);
+                    this.bn.removeRight(metabolite, compartment, r);
+                }
+            }));
 
             this.bn.removeOnCascade(compartment);
 
@@ -177,20 +180,24 @@ public class SetIdsFromFile extends AbstractSetAttributesFromFile {
         BioReaction reaction = this.bn.getReaction(id);
 
         if (reaction != null) {
-            BioReaction newReaction = new BioReaction(reaction, newId);
-            this.bn.add(newReaction);
+            BioReaction newReaction;
+
+            if (!this.bn.containsReaction(newId)) {
+                newReaction = new BioReaction(reaction, newId);
+                this.bn.add(newReaction);
+                // Add lefts and rights
+                this.bn.affectLeft(newReaction, reaction.getLeftReactantsView());
+                this.bn.affectRight(newReaction, reaction.getRightReactantsView());
+            } else {
+                newReaction = this.bn.getReaction(newId);
+                System.err.println("[WARNING] The reaction "+newId+" already exists");
+            }
 
             // Add enzymes
             this.bn.affectEnzyme(newReaction, reaction.getEnzymesView());
 
             // Change reaction in the pathways
-            this.bn.getPathwaysFromReaction(reaction).forEach(p -> {
-                this.bn.affectToPathway(p, newReaction);
-            });
-
-            // Add lefts and rights
-            this.bn.affectLeft(newReaction, reaction.getLeftReactantsView());
-            this.bn.affectRight(newReaction, reaction.getRightReactantsView());
+            this.bn.getPathwaysFromReaction(reaction).forEach(p -> this.bn.affectToPathway(p, newReaction));
 
             this.bn.removeOnCascade(reaction);
         }
@@ -206,24 +213,28 @@ public class SetIdsFromFile extends AbstractSetAttributesFromFile {
         BioMetabolite metabolite = this.bn.getMetabolite(id);
 
         if (metabolite != null) {
-            BioMetabolite newMetabolite = new BioMetabolite(metabolite, newId);
-            this.bn.add(newMetabolite);
-
-            // Change the metabolite in the compartments
-            this.bn.getCompartmentsView().stream().filter(c -> c.getComponentsView().contains(metabolite)).forEach(c -> {
-                this.bn.removeFromCompartment(c, metabolite);
-                this.bn.affectToCompartment(c, newMetabolite);
-            });
+            BioMetabolite newMetabolite;
+
+            if (!this.bn.containsMetabolite(newId)) {
+                newMetabolite = new BioMetabolite(metabolite, newId);
+                this.bn.add(newMetabolite);
+                // Change the metabolite in the compartments
+                this.bn.getCompartmentsView().stream().filter(c -> c.getComponentsView().contains(metabolite)).forEach(c -> {
+                    this.bn.removeFromCompartment(c, metabolite);
+                    this.bn.affectToCompartment(c, newMetabolite);
+                });
+            } else {
+                newMetabolite = this.bn.getMetabolite(newId);
+                System.err.println("[WARNING] The metabolite "+newId+" already exists");
+            }
 
             // Change the metabolite in the left reactants
-            this.bn.getReactionsView().forEach(r -> {
-                r.getLeftReactantsView().stream().filter(reactant -> reactant.getMetabolite().equals(metabolite))
-                        .forEach(reactant -> {
-                            Double sto = reactant.getQuantity();
-                            BioCompartment cpt = reactant.getLocation();
-                            this.bn.affectLeft(r, sto, cpt, newMetabolite);
-                        });
-            });
+            this.bn.getReactionsView().forEach(r -> r.getLeftReactantsView().stream().filter(reactant -> reactant.getMetabolite().equals(metabolite))
+                    .forEach(reactant -> {
+                        Double sto = reactant.getQuantity();
+                        BioCompartment cpt = reactant.getLocation();
+                        this.bn.affectLeft(r, sto, cpt, newMetabolite);
+                    }));
 
             // Change the metabolite in the right reactants
             this.bn.getReactionsView().forEach(r ->
@@ -239,8 +250,8 @@ public class SetIdsFromFile extends AbstractSetAttributesFromFile {
             this.bn.getEnzymesView().
                     forEach(e -> e.getParticipantsView()
                             .stream().filter(p -> p.getPhysicalEntity().equals(metabolite))
-                            .forEach(partcipant -> {
-                                Double sto = partcipant.getQuantity();
+                            .forEach(participant -> {
+                                Double sto = participant.getQuantity();
                                 this.bn.affectSubUnit(e, sto, newMetabolite);
                             }));
 
@@ -258,8 +269,16 @@ public class SetIdsFromFile extends AbstractSetAttributesFromFile {
         BioPathway originalPathway = this.bn.getPathway(id);
 
         if (originalPathway != null) {
-            BioPathway newPathway = new BioPathway(originalPathway, newId);
-            this.bn.add(newPathway);
+            BioPathway newPathway;
+
+            if (!this.bn.containsPathway(newId)) {
+                newPathway = new BioPathway(originalPathway, newId);
+                this.bn.add(newPathway);
+            } else {
+                newPathway = this.bn.getPathway(newId);
+                System.err.println("[WARNING] The pathway "+newId+" already exists");
+            }
+
             this.bn.affectToPathway(newPathway, this.bn.getReactionsFromPathways(originalPathway));
             this.bn.removeOnCascade(originalPathway);
         }
@@ -268,18 +287,24 @@ public class SetIdsFromFile extends AbstractSetAttributesFromFile {
     /**
      * Replace a gene with the same gene with another id
      *
-     * @param id    : the origina id
+     * @param id    : the original id
      * @param newId : the new id
      */
     private void setGeneId(String id, String newId) {
         BioGene originalGene = this.bn.getGene(id);
 
         if (originalGene != null) {
-            BioGene newGene = new BioGene(originalGene, newId);
-            this.bn.add(newGene);
-            this.bn.getProteinsView().stream().filter(p -> p.getGene().equals(originalGene)).forEach(p -> {
-                this.bn.affectGeneProduct(p, newGene);
-            });
+            BioGene newGene;
+            if (!this.bn.containsGene(newId)) {
+                newGene = new BioGene(originalGene, newId);
+                this.bn.add(newGene);
+            } else {
+                newGene = this.bn.getGene(id);
+                System.err.println("[WARNING] The gene "+newId+" already exists");
+            }
+
+            this.bn.getProteinsView().stream().filter(p -> p.getGene().equals(originalGene)).forEach(p -> this.bn.affectGeneProduct(p, newGene));
+
             this.bn.removeOnCascade(originalGene);
         }
     }
diff --git a/met4j-io/src/test/java/fr/inrae/toulouse/metexplore/met4j_io/tabulated/attributes/SetIdsFromFileTest.java b/met4j-io/src/test/java/fr/inrae/toulouse/metexplore/met4j_io/tabulated/attributes/SetIdsFromFileTest.java
index 579b835b8e6338d108dad6656a12dac44151ce6d..e5d1d32e28e21d41bc3f7dd17f17f1523880234a 100644
--- a/met4j-io/src/test/java/fr/inrae/toulouse/metexplore/met4j_io/tabulated/attributes/SetIdsFromFileTest.java
+++ b/met4j-io/src/test/java/fr/inrae/toulouse/metexplore/met4j_io/tabulated/attributes/SetIdsFromFileTest.java
@@ -83,6 +83,22 @@ public class SetIdsFromFileTest {
 
         assertEquals(1, network.getProteinsView().size());
 
+        // Test when the gene is already present
+        network.add(new BioGene("g1"));
+
+        setIdsFromFile = Mockito.spy(new SetIdsFromFile(0, 1, network, "", "", 0, EntityType.GENE,false, false));
+
+        Mockito.doReturn(true).when(setIdsFromFile).parseAttributeFile();
+
+        flag = setIdsFromFile.parseLine(line, 1);
+
+        assertTrue(flag);
+
+        setIdsFromFile.setAttributes();
+
+        assertEquals(1, network.getGenesView().size());
+
+
     }
 
     @Test
@@ -136,6 +152,21 @@ public class SetIdsFromFileTest {
         assertTrue(e.getParticipantsView().stream().anyMatch(p -> p.getPhysicalEntity().equals(newMetabolite)));
         assertFalse(e.getParticipantsView().stream().anyMatch(p -> p.getPhysicalEntity().equals(metabolite)));
 
+        // Test when the metabolite is already present
+        network.add(new BioMetabolite("m1"));
+
+        setIdsFromFile = Mockito.spy(new SetIdsFromFile(0, 1, network, "", "", 0, EntityType.METABOLITE,false, false));
+
+        Mockito.doReturn(true).when(setIdsFromFile).parseAttributeFile();
+
+        flag = setIdsFromFile.parseLine(line, 1);
+
+        assertTrue(flag);
+
+        setIdsFromFile.setAttributes();
+
+        assertEquals(1, network.getMetabolitesView().size());
+
     }
 
     @Test
@@ -165,6 +196,21 @@ public class SetIdsFromFileTest {
         assertNotNull(newPathway);
         assertEquals(2, network.getReactionsFromPathways(newPathway).size());
 
+        // test when the pathway is already present
+        network.add(new BioPathway("p1"));
+
+        setIdsFromFile = Mockito.spy(new SetIdsFromFile(0, 1, network, "", "", 0, EntityType.PATHWAY,false, false));
+
+        Mockito.doReturn(true).when(setIdsFromFile).parseAttributeFile();
+
+        flag = setIdsFromFile.parseLine(line, 1);
+
+        assertTrue(flag);
+
+        setIdsFromFile.setAttributes();
+
+        assertEquals(1, network.getPathwaysView().size());
+
     }
 
     @Test
@@ -212,6 +258,22 @@ public class SetIdsFromFileTest {
 
         assertEquals(1, newReaction.getLeftsView().size());
         assertEquals(1, newReaction.getRightsView().size());
+
+        // Check if the reaction is already in the network
+        this.network.add(new BioReaction("r1"));
+
+        setIdsFromFile = Mockito.spy(new SetIdsFromFile(0, 1, network, "", "", 0, EntityType.REACTION,false, false));
+
+        Mockito.doReturn(true).when(setIdsFromFile).parseAttributeFile();
+
+        flag = setIdsFromFile.parseLine(line, 1);
+
+        assertTrue(flag);
+
+        setIdsFromFile.setAttributes();
+
+        assertEquals(1, network.getReactionsView().size());
+
     }
 
     @Test
@@ -256,5 +318,20 @@ public class SetIdsFromFileTest {
         assertTrue(r1.getLeftReactantsView().stream().allMatch(reactant -> reactant.getLocation().equals(newCpt)));
         assertTrue(r1.getRightReactantsView().stream().allMatch(reactant -> reactant.getLocation().equals(newCpt)));
 
+        // Check if the compartment is already in the network
+        this.network.add(new BioCompartment("cpt"));
+
+        setIdsFromFile = Mockito.spy(new SetIdsFromFile(0, 1, network, "", "", 0, EntityType.COMPARTMENT,false, false));
+
+        Mockito.doReturn(true).when(setIdsFromFile).parseAttributeFile();
+
+        flag = setIdsFromFile.parseLine(line, 1);
+
+        assertTrue(flag);
+
+        setIdsFromFile.setAttributes();
+
+        assertEquals(1, this.network.getCompartmentsView().size());
+
     }
 }
\ No newline at end of file
diff --git a/met4j-mapping/pom.xml b/met4j-mapping/pom.xml
index c2262605df07896b6febbacab9fcfc9a74a60796..647b884c14b7b1c8ca239b3041f823e1bb595727 100644
--- a/met4j-mapping/pom.xml
+++ b/met4j-mapping/pom.xml
@@ -21,11 +21,6 @@
             <artifactId>met4j-core</artifactId>
             <version>1.2.0-SNAPSHOT</version>
         </dependency>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <version>4.12</version>
-        </dependency>
     </dependencies>
 
     <build>
diff --git a/met4j-mathUtils/pom.xml b/met4j-mathUtils/pom.xml
index 4b36813caef46e0d29543cf0eabf4b2428d1cfd1..d1ce169ef310f8d843a915b653f4ef85f5139e64 100644
--- a/met4j-mathUtils/pom.xml
+++ b/met4j-mathUtils/pom.xml
@@ -22,12 +22,6 @@
     </properties>
 
     <dependencies>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <version>4.12</version>
-            <scope>test</scope>
-        </dependency>
         <dependency>
             <groupId>org.ejml</groupId>
             <artifactId>all</artifactId>
diff --git a/met4j-reconstruction/pom.xml b/met4j-reconstruction/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3404a2d2df5476337e2fb69f680fcd959790802a
--- /dev/null
+++ b/met4j-reconstruction/pom.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright INRAE (2022)
+  ~
+  ~ contact-metexplore@inrae.fr
+  ~
+  ~ This software is a computer program whose purpose is to [describe
+  ~ functionalities and technical features of your software].
+  ~
+  ~ This software is governed by the CeCILL license under French law and
+  ~ abiding by the rules of distribution of free software.  You can  use,
+  ~ modify and/ or redistribute the software under the terms of the CeCILL
+  ~ license as circulated by CEA, CNRS and INRIA at the following URL
+  ~ "https://cecill.info/licences/Licence_CeCILL_V2.1-en.html".
+  ~
+  ~ As a counterpart to the access to the source code and  rights to copy,
+  ~ modify and redistribute granted by the license, users are provided only
+  ~ with a limited warranty  and the software's author,  the holder of the
+  ~ economic rights,  and the successive licensors  have only  limited
+  ~ liability.
+  ~
+  ~ In this respect, the user's attention is drawn to the risks associated
+  ~ with loading,  using,  modifying and/or developing or reproducing the
+  ~ software by the user in light of its specific status of free software,
+  ~ that may mean  that it is complicated to manipulate,  and  that  also
+  ~ therefore means  that it is reserved for developers  and  experienced
+  ~ professionals having in-depth computer knowledge. Users are therefore
+  ~ encouraged to load and test the software's suitability as regards their
+  ~ requirements in conditions enabling the security of their systems and/or
+  ~ data to be ensured and,  more generally, to use and operate it in the
+  ~ same conditions as regards security.
+  ~
+  ~ The fact that you are presently reading this means that you have had
+  ~ knowledge of the CeCILL license and that you accept its terms.
+  ~
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>met4j</artifactId>
+        <groupId>fr.inrae.toulouse.metexplore</groupId>
+        <version>1.2.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>met4j-reconstruction</artifactId>
+    <packaging>jar</packaging>
+
+    <name>met4j-reconstruction</name>
+    <url>http://maven.apache.org</url>
+
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>fr.inrae.toulouse.metexplore</groupId>
+            <artifactId>met4j-io</artifactId>
+            <version>1.2.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <version>0.8.7</version>
+                <executions>
+                    <!-- to avoid bugs in some situations -->
+                    <execution>
+                        <id>default-prepare-agent</id>
+                        <goals>
+                            <goal>prepare-agent</goal>
+                        </goals>
+                    </execution>
+
+                    <!-- create report during maven verify phase -->
+                    <execution>
+                        <id>report</id>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/met4j-reconstruction/src/main/java/fr/inrae/toulouse/metexplore/met4j_reconstruction/check/balance/NetworkBalanceAnalysis.java b/met4j-reconstruction/src/main/java/fr/inrae/toulouse/metexplore/met4j_reconstruction/check/balance/NetworkBalanceAnalysis.java
new file mode 100644
index 0000000000000000000000000000000000000000..8c027af44a56c0fe1a588aaf985bf34ebf8366c7
--- /dev/null
+++ b/met4j-reconstruction/src/main/java/fr/inrae/toulouse/metexplore/met4j_reconstruction/check/balance/NetworkBalanceAnalysis.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright INRAE (2022)
+ *
+ * contact-metexplore@inrae.fr
+ *
+ * This software is a computer program whose purpose is to [describe
+ * functionalities and technical features of your software].
+ *
+ * This software is governed by the CeCILL license under French law and
+ * abiding by the rules of distribution of free software.  You can  use,
+ * modify and/ or redistribute the software under the terms of the CeCILL
+ * license as circulated by CEA, CNRS and INRIA at the following URL
+ * "https://cecill.info/licences/Licence_CeCILL_V2.1-en.html".
+ *
+ * As a counterpart to the access to the source code and  rights to copy,
+ * modify and redistribute granted by the license, users are provided only
+ * with a limited warranty  and the software's author,  the holder of the
+ * economic rights,  and the successive licensors  have only  limited
+ * liability.
+ *
+ * In this respect, the user's attention is drawn to the risks associated
+ * with loading,  using,  modifying and/or developing or reproducing the
+ * software by the user in light of its specific status of free software,
+ * that may mean  that it is complicated to manipulate,  and  that  also
+ * therefore means  that it is reserved for developers  and  experienced
+ * professionals having in-depth computer knowledge. Users are therefore
+ * encouraged to load and test the software's suitability as regards their
+ * requirements in conditions enabling the security of their systems and/or
+ * data to be ensured and,  more generally, to use and operate it in the
+ * same conditions as regards security.
+ *
+ * The fact that you are presently reading this means that you have had
+ * knowledge of the CeCILL license and that you accept its terms.
+ *
+ */
+package fr.inrae.toulouse.metexplore.met4j_reconstruction.check.balance;
+
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioEntity;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork;
+import lombok.Getter;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Performs the balance checking on all the reactions of a BioNetwork
+ */
+public class NetworkBalanceAnalysis {
+
+    @Getter
+    private final List<ReactionBalanceAnalysis> allBalances;
+    @Getter
+    private final List<ReactionBalanceAnalysis> balanced;
+    @Getter
+    private final List<ReactionBalanceAnalysis> unbalanced;
+
+    /**
+     * Constructor
+     *
+     * @param network a BioNetwork
+     */
+    public NetworkBalanceAnalysis(BioNetwork network) {
+
+        this.allBalances = network.getReactionsView().stream()
+                .sorted(Comparator.comparing(BioEntity::getId))
+                .map(ReactionBalanceAnalysis::new)
+                .collect(Collectors.toList());
+
+        this.balanced = this.allBalances.stream().filter(ReactionBalanceAnalysis::isBalanced).collect(Collectors.toList());
+        this.unbalanced = this.allBalances.stream().filter(b -> !b.isBalanced() && !b.isExchange()).collect(Collectors.toList());
+
+    }
+
+
+}
+
+
diff --git a/met4j-reconstruction/src/main/java/fr/inrae/toulouse/metexplore/met4j_reconstruction/check/balance/ReactionBalanceAnalysis.java b/met4j-reconstruction/src/main/java/fr/inrae/toulouse/metexplore/met4j_reconstruction/check/balance/ReactionBalanceAnalysis.java
new file mode 100644
index 0000000000000000000000000000000000000000..a418b9fe1f560a696ab4142af9e1b7b4961a6b55
--- /dev/null
+++ b/met4j-reconstruction/src/main/java/fr/inrae/toulouse/metexplore/met4j_reconstruction/check/balance/ReactionBalanceAnalysis.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright INRAE (2022)
+ *
+ * contact-metexplore@inrae.fr
+ *
+ * This software is a computer program whose purpose is to [describe
+ * functionalities and technical features of your software].
+ *
+ * This software is governed by the CeCILL license under French law and
+ * abiding by the rules of distribution of free software.  You can  use,
+ * modify and/ or redistribute the software under the terms of the CeCILL
+ * license as circulated by CEA, CNRS and INRIA at the following URL
+ * "https://cecill.info/licences/Licence_CeCILL_V2.1-en.html".
+ *
+ * As a counterpart to the access to the source code and  rights to copy,
+ * modify and redistribute granted by the license, users are provided only
+ * with a limited warranty  and the software's author,  the holder of the
+ * economic rights,  and the successive licensors  have only  limited
+ * liability.
+ *
+ * In this respect, the user's attention is drawn to the risks associated
+ * with loading,  using,  modifying and/or developing or reproducing the
+ * software by the user in light of its specific status of free software,
+ * that may mean  that it is complicated to manipulate,  and  that  also
+ * therefore means  that it is reserved for developers  and  experienced
+ * professionals having in-depth computer knowledge. Users are therefore
+ * encouraged to load and test the software's suitability as regards their
+ * requirements in conditions enabling the security of their systems and/or
+ * data to be ensured and,  more generally, to use and operate it in the
+ * same conditions as regards security.
+ *
+ * The fact that you are presently reading this means that you have had
+ * knowledge of the CeCILL license and that you accept its terms.
+ *
+ */
+package fr.inrae.toulouse.metexplore.met4j_reconstruction.check.balance;
+
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioMetabolite;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioReactant;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioReaction;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.collection.BioCollection;
+import fr.inrae.toulouse.metexplore.met4j_core.utils.StringUtils;
+import fr.inrae.toulouse.metexplore.met4j_io.annotations.metabolite.MetaboliteAttributes;
+import lombok.Getter;
+
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Class to store the result of a reaction balance checking
+ */
+public class ReactionBalanceAnalysis
+{
+    @Getter
+    private HashMap<String, Double> balances;
+    @Getter
+    private final BioReaction reaction;
+    @Getter
+    private final BioCollection<BioMetabolite> metabolitesWithBadFormula;
+
+    public ReactionBalanceAnalysis(BioReaction reaction) {
+        this.reaction = reaction;
+
+        this.metabolitesWithBadFormula = new BioCollection<>();
+
+        this.computeBalances();
+    }
+
+    private void computeBalances() {
+
+        HashMap<String, Double> tmpBalances = new HashMap<>();
+
+        for(BioReactant left : reaction.getLeftReactantsView()) {
+            boolean flag = this.countAtoms(tmpBalances, left, false);
+            if(!flag) {
+                metabolitesWithBadFormula.add(left.getMetabolite());
+            }
+        }
+        for(BioReactant right : reaction.getRightReactantsView()) {
+            boolean flag = this.countAtoms(tmpBalances, right, true);
+            if(!flag) {
+                metabolitesWithBadFormula.add(right.getMetabolite());
+            }
+        }
+
+        this.balances = tmpBalances;
+    }
+
+    private boolean countAtoms(HashMap<String, Double> tmpBalances, BioReactant reactant, Boolean isRight) {
+
+        Double sto = isRight ? reactant.getQuantity() : - reactant.getQuantity();
+
+        String formula = reactant.getMetabolite().getChemicalFormula();
+
+        if(! StringUtils.checkMetaboliteFormula(formula)) {
+            return false;
+        }
+
+        String REGEX = "([A-Z][a-z]*)([0-9]*)";
+
+        Pattern pattern = Pattern.compile(REGEX);
+        Matcher matcher = pattern.matcher(formula);
+
+        while (matcher.find()) {
+
+            String atom = matcher.group(1);
+
+            String numStr = matcher.group(2);
+
+            if (numStr.equals("")) {
+                numStr = "1.0";
+            }
+
+            Double number = Double.parseDouble(numStr);
+
+            if (!tmpBalances.containsKey(atom)) {
+                tmpBalances.put(atom, sto * number);
+            } else {
+                tmpBalances.put(atom, tmpBalances.get(atom) + sto * number);
+            }
+
+        }
+
+        return true;
+    }
+
+    /**
+     * @return true if the reaction is balanced
+     */
+    public boolean isBalanced() {
+        return metabolitesWithBadFormula.size() == 0 && balances.values().stream().reduce(0.0, Double::sum).equals(0.0);
+    }
+
+    /**
+     * check if the reaction is an exchange reaction, ie :
+     * - has an empty side
+     * - or contains a metabolite that has boundary condition equals to true
+     * @return a boolean
+     */
+    public boolean isExchange() {
+        if(reaction.getLeftReactantsView().size() == 0 || reaction.getRightReactantsView().size() == 0)
+        {
+            return true;
+        }
+        return reaction.getMetabolitesView().stream().anyMatch(MetaboliteAttributes::getBoundaryCondition);
+
+    }
+
+}
diff --git a/met4j-reconstruction/src/test/java/fr/inrae/toulouse/metexplore/met4j_reconstruction/check/balance/NetworkBalanceAnalysisTest.java b/met4j-reconstruction/src/test/java/fr/inrae/toulouse/metexplore/met4j_reconstruction/check/balance/NetworkBalanceAnalysisTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d9057f059956acd6ed8d4e8ac9970e663f5b56bc
--- /dev/null
+++ b/met4j-reconstruction/src/test/java/fr/inrae/toulouse/metexplore/met4j_reconstruction/check/balance/NetworkBalanceAnalysisTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright INRAE (2022)
+ *
+ * contact-metexplore@inrae.fr
+ *
+ * This software is a computer program whose purpose is to [describe
+ * functionalities and technical features of your software].
+ *
+ * This software is governed by the CeCILL license under French law and
+ * abiding by the rules of distribution of free software.  You can  use,
+ * modify and/ or redistribute the software under the terms of the CeCILL
+ * license as circulated by CEA, CNRS and INRIA at the following URL
+ * "https://cecill.info/licences/Licence_CeCILL_V2.1-en.html".
+ *
+ * As a counterpart to the access to the source code and  rights to copy,
+ * modify and redistribute granted by the license, users are provided only
+ * with a limited warranty  and the software's author,  the holder of the
+ * economic rights,  and the successive licensors  have only  limited
+ * liability.
+ *
+ * In this respect, the user's attention is drawn to the risks associated
+ * with loading,  using,  modifying and/or developing or reproducing the
+ * software by the user in light of its specific status of free software,
+ * that may mean  that it is complicated to manipulate,  and  that  also
+ * therefore means  that it is reserved for developers  and  experienced
+ * professionals having in-depth computer knowledge. Users are therefore
+ * encouraged to load and test the software's suitability as regards their
+ * requirements in conditions enabling the security of their systems and/or
+ * data to be ensured and,  more generally, to use and operate it in the
+ * same conditions as regards security.
+ *
+ * The fact that you are presently reading this means that you have had
+ * knowledge of the CeCILL license and that you accept its terms.
+ *
+ */
+
+package fr.inrae.toulouse.metexplore.met4j_reconstruction.check.balance;
+
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioCompartment;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioMetabolite;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioReaction;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+public class NetworkBalanceAnalysisTest {
+
+    public static BioNetwork network;
+    public static BioReaction r1, r2, r3;
+    public static BioMetabolite s1;
+    public static BioMetabolite s2;
+    public static BioMetabolite p1;
+    public static BioMetabolite p2;
+    private static NetworkBalanceAnalysis analysis;
+
+    @BeforeClass
+    public static void init() {
+
+        network = new BioNetwork();
+        s1 = new BioMetabolite("s1");
+        s2 = new BioMetabolite("s2");
+        p1 = new BioMetabolite("p1");
+        p2 = new BioMetabolite("p2");
+        r1 = new BioReaction("r1");
+        r2 = new BioReaction("r2");
+        r3 = new BioReaction("r03");
+
+        s1.setChemicalFormula("C6H6O3");
+        s2.setChemicalFormula("C2H3");
+        p1.setChemicalFormula("C2H2O");
+        p2.setChemicalFormula("C4H6");
+
+        BioCompartment cpt = new BioCompartment("cpt");
+
+        network.add(s1, s2, p1, p2, r1, r2, r3, cpt);
+        network.affectToCompartment(cpt, s1, s2, p1, p2);
+
+        network.affectLeft(r1, 1.0, cpt, s1);
+        network.affectLeft(r1, 2.0, cpt, s2);
+        network.affectRight(r1, 3.0, cpt, p1);
+        network.affectRight(r1, 1.0, cpt, p2);
+
+        network.affectLeft(r2, 1.0, cpt, s1);
+        network.affectRight(r2, 3.0, cpt, p1);
+        network.affectRight(r2, 1.0, cpt, p2);
+
+        network.affectLeft(r3, 1.0, cpt, s1);
+        network.affectRight(r3, 3.0, cpt, p1);
+
+        // r1 : s1 + 2 s2 -> 3 p1 + 1 p2    // balanced
+        // r2 : s1 -> 3 p1 + 1 p2           // not balanced
+        // r3 : s1 -> 3 p1                  // balanced
+
+        analysis = new NetworkBalanceAnalysis(network);
+
+
+    }
+
+
+    @Test
+    public void getAllBalances() {
+        List<ReactionBalanceAnalysis> allBalances = analysis.getAllBalances();
+
+        assertEquals(3, allBalances.size());
+    }
+
+    @Test
+    public void getBalanced() {
+        List<ReactionBalanceAnalysis> balanced = analysis.getBalanced();
+
+        assertEquals(2, balanced.size());
+    }
+
+    @Test
+    public void getUnbalanced() {
+        List<ReactionBalanceAnalysis> unbalanced = analysis.getUnbalanced();
+
+        assertEquals(1, unbalanced.size());
+    }
+}
\ No newline at end of file
diff --git a/met4j-reconstruction/src/test/java/fr/inrae/toulouse/metexplore/met4j_reconstruction/check/balance/ReactionBalanceAnalysisTest.java b/met4j-reconstruction/src/test/java/fr/inrae/toulouse/metexplore/met4j_reconstruction/check/balance/ReactionBalanceAnalysisTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4bc8f96f29e712034403a765fa0b0f81145f0d36
--- /dev/null
+++ b/met4j-reconstruction/src/test/java/fr/inrae/toulouse/metexplore/met4j_reconstruction/check/balance/ReactionBalanceAnalysisTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright INRAE (2022)
+ *
+ * contact-metexplore@inrae.fr
+ *
+ * This software is a computer program whose purpose is to [describe
+ * functionalities and technical features of your software].
+ *
+ * This software is governed by the CeCILL license under French law and
+ * abiding by the rules of distribution of free software.  You can  use,
+ * modify and/ or redistribute the software under the terms of the CeCILL
+ * license as circulated by CEA, CNRS and INRIA at the following URL
+ * "https://cecill.info/licences/Licence_CeCILL_V2.1-en.html".
+ *
+ * As a counterpart to the access to the source code and  rights to copy,
+ * modify and redistribute granted by the license, users are provided only
+ * with a limited warranty  and the software's author,  the holder of the
+ * economic rights,  and the successive licensors  have only  limited
+ * liability.
+ *
+ * In this respect, the user's attention is drawn to the risks associated
+ * with loading,  using,  modifying and/or developing or reproducing the
+ * software by the user in light of its specific status of free software,
+ * that may mean  that it is complicated to manipulate,  and  that  also
+ * therefore means  that it is reserved for developers  and  experienced
+ * professionals having in-depth computer knowledge. Users are therefore
+ * encouraged to load and test the software's suitability as regards their
+ * requirements in conditions enabling the security of their systems and/or
+ * data to be ensured and,  more generally, to use and operate it in the
+ * same conditions as regards security.
+ *
+ * The fact that you are presently reading this means that you have had
+ * knowledge of the CeCILL license and that you accept its terms.
+ *
+ */
+
+package fr.inrae.toulouse.metexplore.met4j_reconstruction.check.balance;
+
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioCompartment;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioMetabolite;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioReaction;
+import fr.inrae.toulouse.metexplore.met4j_io.annotations.metabolite.MetaboliteAttributes;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashMap;
+
+import static org.junit.Assert.*;
+
+public class ReactionBalanceAnalysisTest {
+
+    private static BioNetwork network;
+    private static BioReaction reaction;
+    private static BioMetabolite s1;
+    private static BioMetabolite s2;
+    private static BioMetabolite p1;
+    private static BioMetabolite p2;
+    private static BioCompartment cpt;
+
+    @Before
+    public void init() {
+
+        network = new BioNetwork();
+        s1 = new BioMetabolite("s1");
+        s2 = new BioMetabolite("s2");
+        p1 = new BioMetabolite("p1");
+        p2 = new BioMetabolite("p2");
+        reaction = new BioReaction("r1");
+        cpt = new BioCompartment("cpt");
+
+        network.add(s1, s2, p1, p2, reaction, cpt);
+        network.affectToCompartment(cpt, s1, s2, p1, p2);
+        network.affectLeft(reaction, 1.0, cpt, s1);
+        network.affectLeft(reaction, 2.0, cpt, s2);
+        network.affectRight(reaction, 3.0, cpt, p1);
+        network.affectRight(reaction, 1.0, cpt, p2);
+
+        // reaction : s1 + 2 s2 -> 3 p1 + 1 p2
+    }
+
+    public void createBalanced() {
+        s1.setChemicalFormula("C6H6O3");
+        s2.setChemicalFormula("C2H3");
+        p1.setChemicalFormula("C2H2O");
+        p2.setChemicalFormula("C4H6");
+
+    }
+
+    public void createUnbalanced() {
+        s1.setChemicalFormula("C6H6O3");
+        s2.setChemicalFormula("C2H3");
+        p1.setChemicalFormula("CH2O");
+        p2.setChemicalFormula("C4H6");
+
+    }
+
+    public void createBalancedWithNull() {
+        s1.setChemicalFormula("C6H9O3");
+        s2.setChemicalFormula(null);
+        p1.setChemicalFormula("C2H2O");
+        p2.setChemicalFormula("H3");
+    }
+
+    public void createBalancedWithBadFormula() {
+        s1.setChemicalFormula("C6H9O3");
+        s2.setChemicalFormula("2C");
+        p1.setChemicalFormula("C2H2O");
+        p2.setChemicalFormula("H3");
+    }
+
+    @Test
+    public void isBalanced() {
+        this.createBalanced();
+        ReactionBalanceAnalysis balance = new ReactionBalanceAnalysis(reaction);
+
+        assertTrue(balance.isBalanced());
+
+        this.createUnbalanced();
+
+        balance = new ReactionBalanceAnalysis(reaction);
+
+        assertFalse(balance.isBalanced());
+    }
+
+    @Test
+    public void isBalancedWithNull() {
+        this.createBalancedWithNull();
+        ReactionBalanceAnalysis balance = new ReactionBalanceAnalysis(reaction);
+
+        assertFalse(balance.isBalanced());
+
+        assertTrue(balance.getMetabolitesWithBadFormula().contains(s2));
+    }
+
+    @Test
+    public void isBalancedWithBadFormula() {
+        this.createBalancedWithBadFormula();
+        ReactionBalanceAnalysis balance = new ReactionBalanceAnalysis(reaction);
+
+        assertFalse(balance.isBalanced());
+
+        assertTrue(balance.getMetabolitesWithBadFormula().contains(s2));
+    }
+
+    @Test
+    public void getBalances() {
+        this.createBalanced();
+        ReactionBalanceAnalysis balance = new ReactionBalanceAnalysis(reaction);
+        HashMap<String, Double> test = balance.getBalances();
+
+        HashMap<String, Double> ref = new HashMap<>();
+        ref.put("O", 0.0);
+        ref.put("C", 0.0);
+        ref.put("H", 0.0);
+
+        assertEquals(ref, test);
+
+        this.createUnbalanced();
+        balance = new ReactionBalanceAnalysis(reaction);
+        test = balance.getBalances();
+
+        ref = new HashMap<>();
+        ref.put("O", 0.0);
+        ref.put("C", -3.0);
+        ref.put("H", 0.0);
+
+        assertEquals(ref, test);
+    }
+
+    @Test
+    public void getBalancesWithNull() {
+        this.createBalancedWithNull();
+        ReactionBalanceAnalysis balance = new ReactionBalanceAnalysis(reaction);
+        HashMap<String, Double> test = balance.getBalances();
+
+        HashMap<String, Double> ref = new HashMap<>();
+        ref.put("O", 0.0);
+        ref.put("C", 0.0);
+        ref.put("H", 0.0);
+
+        assertEquals(ref, test);
+    }
+
+    @Test
+    public void getBalancesWithBadFormula() {
+        this.createBalancedWithBadFormula();
+        ReactionBalanceAnalysis balance = new ReactionBalanceAnalysis(reaction);
+        HashMap<String, Double> test = balance.getBalances();
+
+        HashMap<String, Double> ref = new HashMap<>();
+        ref.put("O", 0.0);
+        ref.put("C", 0.0);
+        ref.put("H", 0.0);
+
+        assertEquals(ref, test);
+    }
+
+    @Test
+    public void isExchange() {
+
+        this.createBalanced();
+
+        ReactionBalanceAnalysis balance = new ReactionBalanceAnalysis(reaction);
+
+        assertFalse(balance.isExchange());
+
+        MetaboliteAttributes.setBoundaryCondition(s1, true);
+
+        assertTrue(balance.isExchange());
+
+        MetaboliteAttributes.setBoundaryCondition(s1, false);
+        MetaboliteAttributes.setBoundaryCondition(p1, true);
+
+        assertTrue(balance.isExchange());
+
+        network.removeRight(p1, cpt, reaction);
+        network.removeRight(p2, cpt, reaction);
+
+        assertTrue(balance.isExchange());
+
+
+    }
+}
\ No newline at end of file
diff --git a/met4j-toolbox/pom.xml b/met4j-toolbox/pom.xml
index 40ff14d3b98e646a682ef05091e9ddca9998f934..c656eead9842255ded4c788493b7f2d649c2b6b6 100644
--- a/met4j-toolbox/pom.xml
+++ b/met4j-toolbox/pom.xml
@@ -19,12 +19,6 @@
     </properties>
 
     <dependencies>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <version>4.12</version>
-            <scope>test</scope>
-        </dependency>
         <dependency>
             <groupId>args4j</groupId>
             <artifactId>args4j</artifactId>
@@ -60,6 +54,11 @@
             <artifactId>met4j-mapping</artifactId>
             <version>1.2.0-SNAPSHOT</version>
         </dependency>
+        <dependency>
+            <groupId>fr.inrae.toulouse.metexplore</groupId>
+            <artifactId>met4j-reconstruction</artifactId>
+            <version>1.2.0-SNAPSHOT</version>
+        </dependency>
         <!-- https://mvnrepository.com/artifact/com.googlecode.json-simple/json-simple -->
         <dependency>
             <groupId>com.googlecode.json-simple</groupId>
@@ -80,6 +79,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-shade-plugin</artifactId>
+                <version>3.2.4</version>
                 <executions>
                     <execution>
                         <phase>package</phase>
diff --git a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/attributes/SbmlSetIdsFromFile.java b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/attributes/SbmlSetIdsFromFile.java
index 12def855d3df0696cbcf54cc2d79058eba2d8ef5..dae320521c936fa7e6ef9ed9a25def39052a7ef9 100644
--- a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/attributes/SbmlSetIdsFromFile.java
+++ b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/attributes/SbmlSetIdsFromFile.java
@@ -94,6 +94,7 @@ public class SbmlSetIdsFromFile extends AbstractSbmlSetAny {
         try {
             flag = setter.setAttributes();
         } catch (Exception e) {
+            e.printStackTrace();
             flag=false;
         }
 
diff --git a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/SbmlCheckBalance.java b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/SbmlCheckBalance.java
new file mode 100644
index 0000000000000000000000000000000000000000..f01b4155296cec0f2ba0f7aa18da44c94df90d9c
--- /dev/null
+++ b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/SbmlCheckBalance.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright INRAE (2022)
+ *
+ * contact-metexplore@inrae.fr
+ *
+ * This software is a computer program whose purpose is to [describe
+ * functionalities and technical features of your software].
+ *
+ * This software is governed by the CeCILL license under French law and
+ * abiding by the rules of distribution of free software.  You can  use,
+ * modify and/ or redistribute the software under the terms of the CeCILL
+ * license as circulated by CEA, CNRS and INRIA at the following URL
+ * "https://cecill.info/licences/Licence_CeCILL_V2.1-en.html".
+ *
+ * As a counterpart to the access to the source code and  rights to copy,
+ * modify and redistribute granted by the license, users are provided only
+ * with a limited warranty  and the software's author,  the holder of the
+ * economic rights,  and the successive licensors  have only  limited
+ * liability.
+ *
+ * In this respect, the user's attention is drawn to the risks associated
+ * with loading,  using,  modifying and/or developing or reproducing the
+ * software by the user in light of its specific status of free software,
+ * that may mean  that it is complicated to manipulate,  and  that  also
+ * therefore means  that it is reserved for developers  and  experienced
+ * professionals having in-depth computer knowledge. Users are therefore
+ * encouraged to load and test the software's suitability as regards their
+ * requirements in conditions enabling the security of their systems and/or
+ * data to be ensured and,  more generally, to use and operate it in the
+ * same conditions as regards security.
+ *
+ * The fact that you are presently reading this means that you have had
+ * knowledge of the CeCILL license and that you accept its terms.
+ *
+ */
+package fr.inrae.toulouse.metexplore.met4j_toolbox.reconstruction;
+
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.utils.BioReactionUtils;
+import fr.inrae.toulouse.metexplore.met4j_io.jsbml.reader.JsbmlReader;
+import fr.inrae.toulouse.metexplore.met4j_io.jsbml.reader.Met4jSbmlReaderException;
+
+import fr.inrae.toulouse.metexplore.met4j_reconstruction.check.balance.NetworkBalanceAnalysis;
+import fr.inrae.toulouse.metexplore.met4j_reconstruction.check.balance.ReactionBalanceAnalysis;
+import fr.inrae.toulouse.metexplore.met4j_toolbox.generic.AbstractMet4jApplication;
+import fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.Format;
+import fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.ParameterType;
+import org.kohsuke.args4j.Option;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+
+import static fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.EnumFormats.Sbml;
+import static fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.EnumFormats.Tsv;
+import static fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.EnumParameterTypes.InputFile;
+import static fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.EnumParameterTypes.OutputFile;
+
+public class SbmlCheckBalance extends AbstractMet4jApplication {
+
+    @Format(name = Sbml)
+    @ParameterType(name = InputFile)
+    @Option(name = "-sbml", usage = "Original sbml file", required = true)
+    public String sbml;
+
+    @Format(name = Tsv)
+    @ParameterType(name = OutputFile)
+    @Option(name = "-out", usage = "[checkBalances.tsv] Output tabulated file (1st col: reaction id, 2nd col: " +
+            "boolean indicating if the reaction is balanced, 3rd col: atom balances, 4th col: metabolites with bad formula")
+    public String out = "checkBalances.tsv";
+    private NetworkBalanceAnalysis analysis;
+
+    public static void main(String[] args) {
+        SbmlCheckBalance app = new SbmlCheckBalance();
+        
+        app.parseArguments(args);
+        
+        app.run();
+    }
+
+    private void run() {
+
+        BioNetwork network = this.readSbml();
+
+        analysis = new NetworkBalanceAnalysis(network);
+
+        try {
+            this.writeResults();
+        } catch (IOException e) {
+           e.printStackTrace();
+            System.err.println("[MET4J ERROR] Error while writing the results");
+        }
+
+    }
+
+    private void writeResults() throws IOException {
+
+        List<ReactionBalanceAnalysis> balances = analysis.getAllBalances();
+
+        FileWriter writer = new FileWriter(this.out);
+
+        writer.write("#Id\tisBalanced\tisExchange\tformula\tatom balances\tmetabolites with bad formula\n");
+
+        for (ReactionBalanceAnalysis balance : balances) {
+            String id = balance.getReaction().getId();
+            HashMap<String, Double> atomBalances = balance.getBalances();
+            Set<String> metabolitesWithBadFormula = balance.getMetabolitesWithBadFormula().getIds();
+            String formula = BioReactionUtils.getEquation(balance.getReaction(), false, true);
+
+            writer.write(id + "\t" + balance.isBalanced() + "\t" + balance.isExchange() + "\t" + formula +"\t" + atomBalances + "\t" + (metabolitesWithBadFormula.size() > 0 ? metabolitesWithBadFormula : "") + "\n");
+        }
+
+        writer.close();
+
+    }
+
+    /**
+     * <p>readSbml.</p>
+     *
+     * @return a {@link fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork} object.
+     */
+    private BioNetwork readSbml() {
+        JsbmlReader reader = new JsbmlReader(this.sbml);
+
+        BioNetwork bn = null;
+        try {
+            bn = reader.read();
+        } catch (Met4jSbmlReaderException e) {
+            e.printStackTrace();
+            System.err.println("Problem while reading the sbml file " + this.sbml);
+            System.exit(1);
+        }
+
+        return bn;
+
+    }
+
+    @Override
+    public String getLabel() {
+        return this.getClass().getSimpleName();
+    }
+
+    @Override
+    public String getLongDescription() {
+        return this.getShortDescription()+"\n"+
+                "A reaction is balanced if all its reactants have a chemical formula with a good syntax and if the " +
+                "quantity of each atom is the same in both sides of the reaction.\n" +
+                "For each reaction, indicates if the reaction is balanced, the list of the atoms and the sum of their quantity, and the list of the metabolites " +
+                "that don't have a correct chemical formula.";
+    }
+
+    @Override
+    public String getShortDescription() {
+        return "Check balance of all the reactions in a SBML.";
+    }
+}
diff --git a/pom.xml b/pom.xml
index 93cc4e6d4302573bfea467408ff461cb5c233175..929bc15ad538fc9a9486adf507d046796db45888 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
     <groupId>fr.inrae.toulouse.metexplore</groupId>
@@ -84,6 +85,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
                 <configuration>
                     <source>11</source>
                     <target>11</target>
@@ -173,6 +175,7 @@
         <module>met4j-mathUtils</module>
         <module>met4j-graph</module>
         <module>met4j-mapping</module>
+        <module>met4j-reconstruction</module>
         <module>coverage</module>
         <module>met4j-toolbox</module>
     </modules>
@@ -184,11 +187,17 @@
             <version>3.0.0</version>
             <type>maven-plugin</type>
         </dependency>
-            <dependency>
-                <groupId>org.projectlombok</groupId>
-                <artifactId>lombok</artifactId>
-                <version>1.18.22</version>
-                <scope>provided</scope>
-            </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.22</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.13.2</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
\ No newline at end of file