From 3f1e56d185384f3b4008767f5d15258420472967 Mon Sep 17 00:00:00 2001
From: cfrainay <clement.frainay@inrae.fr>
Date: Tue, 10 May 2022 18:20:03 +0200
Subject: [PATCH 01/17] draft metanetwork creation

---
 .../AbstractMetaNetworkBuilder.java           | 209 ++++++++++++++++++
 .../DefaultMetaEntityFactory.java             |  42 ++++
 .../multinetwork/MetaEntityFactory.java       |  10 +
 .../multinetwork/MetaNetworkBuilder.java      |  45 ++++
 .../multinetwork/MultiNetworkBuilder.java     |  21 ++
 .../MultiOrganNetworkBuilder.java             |  57 +++++
 .../multinetwork/NestedNetworkBuilder.java    |  43 ++++
 .../multinetwork/PanNetworkBuilder.java       |  23 ++
 8 files changed, 450 insertions(+)
 create mode 100644 met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/AbstractMetaNetworkBuilder.java
 create mode 100644 met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/DefaultMetaEntityFactory.java
 create mode 100644 met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaEntityFactory.java
 create mode 100644 met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
 create mode 100644 met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiNetworkBuilder.java
 create mode 100644 met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiOrganNetworkBuilder.java
 create mode 100644 met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/NestedNetworkBuilder.java
 create mode 100644 met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PanNetworkBuilder.java

diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/AbstractMetaNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/AbstractMetaNetworkBuilder.java
new file mode 100644
index 000000000..64ddbcfe4
--- /dev/null
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/AbstractMetaNetworkBuilder.java
@@ -0,0 +1,209 @@
+package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
+
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.*;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.collection.BioCollection;
+import lombok.SneakyThrows;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.*;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+public abstract class AbstractMetaNetworkBuilder implements MultiNetworkBuilder{
+
+    BioCollection<BioNetwork> networks;
+    Map<BioCompartment, Map<BioNetwork, BioCollection<BioMetabolite>>> metaCompComposition;
+    MetaEntityFactory entityFactory;
+
+    Boolean keepGPR = false;
+    Boolean flush;
+    
+    public AbstractMetaNetworkBuilder(){
+        this.entityFactory= new DefaultMetaEntityFactory();
+    }
+
+    public AbstractMetaNetworkBuilder(MetaEntityFactory entityFactory){
+        this.entityFactory=entityFactory;
+    }
+
+    protected void addSharedCompartment(BioCompartment sc) {
+        metaCompComposition.put(sc,new HashMap<>());
+    }
+    protected void addSharedCompartment(BioCompartment sc, BioNetwork... bn) {
+        HashMap<BioNetwork, BioCollection<BioMetabolite>> map = Arrays.stream(bn).collect(Collectors.toMap(
+                x -> x,
+                x -> new BioCollection<BioMetabolite>(),
+                (prev, next) -> {prev.addAll(next);return prev;},
+                HashMap::new
+        ));
+        if(metaCompComposition.containsKey(sc)){
+            metaCompComposition.get(sc).putAll(map);
+        }else{
+            metaCompComposition.put(sc,map);
+        }
+    }
+
+    protected void fuseCompartmentIntoSharedCompartment(BioNetwork n, BioCompartment c, BioCompartment sc) {
+    //TODO
+    }
+
+    protected void bumpCompartmentIntoSharedCompartment(BioNetwork n, BioCompartment c, BioCompartment sc) {
+        Map<BioNetwork, BioCollection<BioMetabolite>> compartmentContent =  metaCompComposition.get(sc);
+        if(compartmentContent==null) throw new IllegalArgumentException("Shared Compartment not found");
+        BioCollection<BioMetabolite> sharedContent = compartmentContent.get(n);
+        if(compartmentContent==null) throw new IllegalArgumentException("SubNetwork not in shared compartment");
+        BioCollection<BioMetabolite> toShare = n.getMetabolitesView().stream()
+                .filter(x -> n.getCompartmentsOf(x).contains(c))
+                .collect(BioCollection::new,BioCollection::add,BioCollection::addAll);
+        sharedContent.addAll(toShare);
+    }
+
+    protected void exchangeWithSharedCompartment(BioNetwork n, BioMetabolite m, BioCompartment sc) {
+        //todo
+    }
+
+    protected void exchangeWithSharedCompartment(BioNetwork n, BioCollection<BioMetabolite> m, BioCompartment sc) {
+        //todo
+    }
+
+    @Override
+    public void add(BioNetwork bn) {
+        networks.add(bn);
+    }
+
+    @Override
+    public abstract void merge();
+
+    @Override
+    public abstract BioNetwork build();
+
+
+    void populateMetaNetwork(BioNetwork meta){
+
+        for(BioNetwork sub : networks){
+
+            Map<BioMetabolite,BioMetabolite> metaboliteConversion = new HashMap<>();
+            Map<BioCompartment,BioCompartment> compartmentConversion = new HashMap<>();
+            Map<BioReaction,BioReaction> reactionConversion = new HashMap<>();
+            Map<BioProtein,BioProtein> proteinConversion = new HashMap<>();
+            Map<BioEnzyme,BioEnzyme> enzymeConversion = new HashMap<>();
+            Map<BioGene,BioGene> geneConversion = new HashMap<>();
+
+            for(BioMetabolite e : sub.getMetabolitesView()){
+                BioMetabolite e2 = entityFactory.copy(e,sub);
+                meta.add(e2);
+                metaboliteConversion.put(e,e2);
+            }
+            for(BioCompartment c : sub.getCompartmentsView()){
+                BioCompartment c2 = entityFactory.copy(c,sub);
+                meta.add(c2);
+                compartmentConversion.put(c,c2);
+                c.getComponentsView().stream().
+                        filter((e) -> e.getClass().equals(BioMetabolite.class)).
+                        forEach((m) -> {
+                            BioMetabolite newMetabolite = metaboliteConversion.get(m);
+                            meta.affectToCompartment(c, newMetabolite);
+                        });
+            }
+
+            // Copy genes
+            if (keepGPR) {
+                //TODO case shared Genome
+                for (BioGene gene : sub.getGenesView()) {
+                    BioGene newGene = entityFactory.copy(gene,sub);
+                    meta.add(newGene);
+                    geneConversion.put(gene,newGene);
+                }
+                for (BioProtein protein : sub.getProteinsView()) {
+                    BioProtein newProtein = entityFactory.copy(protein,sub);
+                    meta.add(newProtein);
+                    proteinConversion.put(protein,newProtein);
+
+                    if (protein.getGene() != null) {
+                        BioGene newGene = geneConversion.get(protein.getGene());
+                        meta.affectGeneProduct(newProtein, newGene);
+                    }
+                }
+                for (BioEnzyme enzyme : sub.getEnzymesView()) {
+
+                    BioEnzyme newEnzyme = entityFactory.copy(enzyme,sub);
+                    meta.add(newEnzyme);
+                    enzymeConversion.put(enzyme,newEnzyme);
+
+                    BioCollection<BioEnzymeParticipant> participants = enzyme.getParticipantsView();
+
+                    for (BioEnzymeParticipant participant : participants) {
+                        Double quantity = participant.getQuantity();
+
+                        if (participant.getPhysicalEntity().getClass().equals(BioMetabolite.class)) {
+                            BioMetabolite metabolite = (BioMetabolite) participant.getPhysicalEntity();
+                            BioMetabolite newMetabolite = metaboliteConversion.get(metabolite);
+                            meta.affectSubUnit(newEnzyme, quantity, newMetabolite);
+                        } else if (participant.getPhysicalEntity().getClass().equals(BioProtein.class)) {
+                            BioProtein protein = (BioProtein) participant.getPhysicalEntity();
+                            BioProtein newProtein  = proteinConversion.get(protein);
+                            meta.affectSubUnit(newEnzyme, quantity, newProtein);
+                        }
+                    }
+                }
+            }
+
+            for (BioReaction r : sub.getReactionsView()) {
+
+                BioReaction newReaction = entityFactory.copy(r,sub);;
+                newReaction.setSpontaneous(r.isSpontaneous());
+                newReaction.setReversible(r.isReversible());
+                newReaction.setEcNumber(r.getEcNumber());
+
+                meta.add(newReaction);
+
+                // Copy lefts
+                for (BioReactant reactant : r.getLeftReactantsView()) {
+                    BioMetabolite newMetabolite = metaboliteConversion.get(reactant.getMetabolite());
+                    BioCompartment newCpt = compartmentConversion.get(reactant.getLocation().getId());
+                    Double sto = reactant.getQuantity();
+                    meta.affectLeft(newReaction, sto, newCpt, newMetabolite);
+                }
+
+                // Copy rights
+                for (BioReactant reactant : r.getRightReactantsView()) {
+                    BioMetabolite newMetabolite = metaboliteConversion.get(reactant.getMetabolite());
+                    BioCompartment newCpt = compartmentConversion.get(reactant.getLocation().getId());
+                    Double sto = reactant.getQuantity();
+                    meta.affectRight(newReaction, sto, newCpt, newMetabolite);
+                }
+
+                // Copy enzymes
+                if (keepGPR) {
+                    for (BioEnzyme enzyme : r.getEnzymesView()) {
+                        BioEnzyme newEnzyme = enzymeConversion.get(enzyme);
+                        meta.affectEnzyme(newReaction, newEnzyme);
+                    }
+                }
+            }
+
+            for (BioPathway pathway : sub.getPathwaysView()) {
+                BioPathway newPathway = entityFactory.copy(pathway,sub);
+                meta.add(newPathway);
+                // Add reactions into pathway
+                BioCollection<BioReaction> reactions = sub.getReactionsFromPathways(pathway);
+
+                for (BioReaction reaction : reactions) {
+                    BioReaction newReaction = reactionConversion.get(reaction);
+                    meta.affectToPathway(newPathway, newReaction);
+                }
+            }
+        }
+    }
+
+
+    void initSharedComp(BioNetwork meta){
+        for(Map.Entry<BioCompartment, Map<BioNetwork, BioCollection<BioMetabolite>>> compDescriptor : metaCompComposition.entrySet()){
+            meta.addCompartment(compDescriptor.getKey());
+            for(Map.Entry<BioNetwork, BioCollection<BioMetabolite>> content : compDescriptor.getValue().entrySet()){
+                meta.affectToCompartment(compDescriptor.getKey(),content.getValue());
+            }
+        }
+    }
+}
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/DefaultMetaEntityFactory.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/DefaultMetaEntityFactory.java
new file mode 100644
index 000000000..ce5227046
--- /dev/null
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/DefaultMetaEntityFactory.java
@@ -0,0 +1,42 @@
+package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
+
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioEntity;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork;
+import lombok.SneakyThrows;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+public class DefaultMetaEntityFactory implements MetaEntityFactory {
+
+    public BiFunction<BioEntity, BioNetwork, String> idSupplier = (e,bn) -> {
+        return e.getId()+"_"+bn.getId();
+    };
+
+    public BiFunction<BioEntity, BioNetwork, String> getIdSupplier() {
+        return idSupplier;
+    }
+
+    public void setIdSupplier(BiFunction<BioEntity, BioNetwork, String> idSupplier) {
+        this.idSupplier = idSupplier;
+    }
+
+    @SneakyThrows
+    @Override
+    public <E extends BioEntity> E copy(E entity, BioNetwork source) {
+        return newEntityInstance(entity, this.idSupplier.apply(entity,source));
+    }
+
+    public static <E extends BioEntity> E newEntityInstance(E entity, String newId) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
+        E newEntity = (E) entity.getClass().getDeclaredConstructor(String.class).newInstance(newId);
+        newEntity.setName(entity.getName());
+        newEntity.setSynonyms(new ArrayList<>(entity.getSynonyms()));
+        newEntity.setComment(entity.getComment());
+        newEntity.setRefs(new HashMap<>(entity.getRefs()));
+        newEntity.setAttributes(new HashMap<>(entity.getAttributes()));
+        return newEntity;
+    }
+}
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaEntityFactory.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaEntityFactory.java
new file mode 100644
index 000000000..554d7b182
--- /dev/null
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaEntityFactory.java
@@ -0,0 +1,10 @@
+package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
+
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioEntity;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork;
+
+public interface MetaEntityFactory {
+
+    public <E extends BioEntity> E copy(E entity, BioNetwork source);
+
+}
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
new file mode 100644
index 000000000..7af6a2b5f
--- /dev/null
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
@@ -0,0 +1,45 @@
+package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
+
+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.collection.BioCollection;
+
+import java.util.UUID;
+
+public class MetaNetworkBuilder extends AbstractMetaNetworkBuilder implements MultiNetworkBuilder{
+
+
+    private BioCompartment medium;
+
+    public MetaNetworkBuilder(BioNetwork superNetwork){
+        setMedium();
+    }
+
+    private void setMedium(){
+        this.medium = new BioCompartment(UUID.randomUUID().toString(),"medium");
+        this.addSharedCompartment(medium);
+    }
+
+    @Override
+    public void add(BioNetwork bn) {
+        super.add(bn);
+        this.addSharedCompartment(medium,this.networks.stream().toArray(BioNetwork[]::new));
+    }
+
+    @Override
+    public void merge() {
+        //todo
+    }
+
+    public void exchangeWithMedium(BioNetwork n, BioMetabolite m){
+        this.exchangeWithSharedCompartment(n, m, medium);
+    };
+
+    @Override
+    public BioNetwork build() {
+        //todo
+        return null;
+    }
+
+}
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiNetworkBuilder.java
new file mode 100644
index 000000000..c17e2cadc
--- /dev/null
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiNetworkBuilder.java
@@ -0,0 +1,21 @@
+package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
+
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork;
+
+public interface MultiNetworkBuilder {
+
+    /**
+     * add network into meta-network
+     * @param bn
+     */
+    public void add(BioNetwork bn);
+
+    public void merge();
+
+    /**
+     * create a new network which encompass all added networks
+     * @return
+     */
+    public BioNetwork build();
+
+}
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiOrganNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiOrganNetworkBuilder.java
new file mode 100644
index 000000000..ae4c1b699
--- /dev/null
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiOrganNetworkBuilder.java
@@ -0,0 +1,57 @@
+package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
+
+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.collection.BioCollection;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class MultiOrganNetworkBuilder extends AbstractMetaNetworkBuilder implements MultiNetworkBuilder{
+
+    @Override
+    public void merge() {
+        //TODO
+    }
+
+
+    @Override
+    public void addSharedCompartment(BioCompartment sc, BioNetwork... bn) {
+        super.addSharedCompartment(sc,bn);
+    }
+
+    @Override
+    public void fuseCompartmentIntoSharedCompartment(BioNetwork n, BioCompartment c, BioCompartment sc) {
+        super.fuseCompartmentIntoSharedCompartment(n,c,sc);
+    }
+
+    @Override
+    public void bumpCompartmentIntoSharedCompartment(BioNetwork n, BioCompartment c, BioCompartment sc) {
+        super.bumpCompartmentIntoSharedCompartment(n,c,sc);
+    }
+
+    @Override
+    public void exchangeWithSharedCompartment(BioNetwork n, BioMetabolite m, BioCompartment sc) {
+        super.exchangeWithSharedCompartment(n,m,sc);
+    }
+
+    @Override
+    public void exchangeWithSharedCompartment(BioNetwork n, BioCollection<BioMetabolite> m, BioCompartment sc) {
+        super.exchangeWithSharedCompartment(n,m,sc);
+    }
+
+    @Override
+    public BioNetwork build() {
+        //todo
+        //create empty meta network
+        BioNetwork meta = new BioNetwork();
+        //add each subnetwork content in dedicated compartment
+        this.populateMetaNetwork(meta);
+        //create shared compartment and the exchanged content
+        this.initSharedComp(meta);
+        return null;
+    }
+}
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/NestedNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/NestedNetworkBuilder.java
new file mode 100644
index 000000000..aeca0c100
--- /dev/null
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/NestedNetworkBuilder.java
@@ -0,0 +1,43 @@
+package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
+
+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;
+
+public class NestedNetworkBuilder extends AbstractMetaNetworkBuilder implements MultiNetworkBuilder{
+
+    private BioNetwork superNet;
+    private BioCompartment superNetComp;
+
+    public NestedNetworkBuilder(BioNetwork superNetwork){
+        setSuperNetwork(superNetwork);
+    }
+
+    private void setSuperNetwork(BioNetwork bn){
+        superNet=bn;
+        this.superNetComp = new BioCompartment(bn.getId());
+        this.addSharedCompartment(superNetComp,superNet);
+    }
+
+    @Override
+    public void add(BioNetwork bn) {
+        super.add(bn);
+        this.addSharedCompartment(superNetComp,this.networks.stream().toArray(BioNetwork[]::new));
+    }
+
+    @Override
+    public void merge() {
+        //todo
+    }
+
+    public void exchangeWithSuperNetwork(BioNetwork n, BioMetabolite m){
+        this.exchangeWithSharedCompartment(n, m, superNetComp);
+    };
+
+    @Override
+    public BioNetwork build() {
+        //todo
+        return null;
+    }
+
+}
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PanNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PanNetworkBuilder.java
new file mode 100644
index 000000000..346f71cbf
--- /dev/null
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PanNetworkBuilder.java
@@ -0,0 +1,23 @@
+package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
+
+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.collection.BioCollection;
+
+public class PanNetworkBuilder implements MultiNetworkBuilder {
+    @Override
+    public void add(BioNetwork bn) {
+        //todo
+    }
+
+    @Override
+    public void merge() {
+        //todo
+    }
+
+    @Override
+    public BioNetwork build() {
+        //todo
+        return null;
+    }
+}
-- 
GitLab


From 297b7681c25ed6758d203be91d54288ada8c42f4 Mon Sep 17 00:00:00 2001
From: cfrainay <clement.frainay@inrae.fr>
Date: Thu, 16 May 2024 16:29:19 +0200
Subject: [PATCH 02/17] finalize metanetwork creation

---
 .../AbstractMetaNetworkBuilder.java           | 209 ------------
 .../multinetwork/CommunityNetworkBuilder.java |  49 +++
 .../DefaultMetaEntityFactory.java             |  42 ---
 .../multinetwork/MetaBioMetabolite.java       |  16 +
 .../multinetwork/MetaEntityFactory.java       |   8 +-
 .../multinetwork/MetaNetworkBuilder.java      | 313 ++++++++++++++++--
 .../multinetwork/MultiNetworkBuilder.java     |   3 +-
 .../MultiOrganNetworkBuilder.java             |  57 ----
 .../multinetwork/NestedNetworkBuilder.java    |  43 ---
 .../multinetwork/PanNetworkBuilder.java       |  23 --
 .../PrefixedMetaEntityFactory.java            |  86 +++++
 .../multinetwork/TestMetaNetworkBuilder.java  | 185 +++++++++++
 12 files changed, 636 insertions(+), 398 deletions(-)
 delete mode 100644 met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/AbstractMetaNetworkBuilder.java
 create mode 100644 met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/CommunityNetworkBuilder.java
 delete mode 100644 met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/DefaultMetaEntityFactory.java
 create mode 100644 met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaBioMetabolite.java
 delete mode 100644 met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiOrganNetworkBuilder.java
 delete mode 100644 met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/NestedNetworkBuilder.java
 delete mode 100644 met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PanNetworkBuilder.java
 create mode 100644 met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java
 create mode 100644 met4j-core/src/test/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/TestMetaNetworkBuilder.java

diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/AbstractMetaNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/AbstractMetaNetworkBuilder.java
deleted file mode 100644
index 64ddbcfe4..000000000
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/AbstractMetaNetworkBuilder.java
+++ /dev/null
@@ -1,209 +0,0 @@
-package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
-
-import fr.inrae.toulouse.metexplore.met4j_core.biodata.*;
-import fr.inrae.toulouse.metexplore.met4j_core.biodata.collection.BioCollection;
-import lombok.SneakyThrows;
-
-import java.lang.reflect.InvocationTargetException;
-import java.util.*;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-public abstract class AbstractMetaNetworkBuilder implements MultiNetworkBuilder{
-
-    BioCollection<BioNetwork> networks;
-    Map<BioCompartment, Map<BioNetwork, BioCollection<BioMetabolite>>> metaCompComposition;
-    MetaEntityFactory entityFactory;
-
-    Boolean keepGPR = false;
-    Boolean flush;
-    
-    public AbstractMetaNetworkBuilder(){
-        this.entityFactory= new DefaultMetaEntityFactory();
-    }
-
-    public AbstractMetaNetworkBuilder(MetaEntityFactory entityFactory){
-        this.entityFactory=entityFactory;
-    }
-
-    protected void addSharedCompartment(BioCompartment sc) {
-        metaCompComposition.put(sc,new HashMap<>());
-    }
-    protected void addSharedCompartment(BioCompartment sc, BioNetwork... bn) {
-        HashMap<BioNetwork, BioCollection<BioMetabolite>> map = Arrays.stream(bn).collect(Collectors.toMap(
-                x -> x,
-                x -> new BioCollection<BioMetabolite>(),
-                (prev, next) -> {prev.addAll(next);return prev;},
-                HashMap::new
-        ));
-        if(metaCompComposition.containsKey(sc)){
-            metaCompComposition.get(sc).putAll(map);
-        }else{
-            metaCompComposition.put(sc,map);
-        }
-    }
-
-    protected void fuseCompartmentIntoSharedCompartment(BioNetwork n, BioCompartment c, BioCompartment sc) {
-    //TODO
-    }
-
-    protected void bumpCompartmentIntoSharedCompartment(BioNetwork n, BioCompartment c, BioCompartment sc) {
-        Map<BioNetwork, BioCollection<BioMetabolite>> compartmentContent =  metaCompComposition.get(sc);
-        if(compartmentContent==null) throw new IllegalArgumentException("Shared Compartment not found");
-        BioCollection<BioMetabolite> sharedContent = compartmentContent.get(n);
-        if(compartmentContent==null) throw new IllegalArgumentException("SubNetwork not in shared compartment");
-        BioCollection<BioMetabolite> toShare = n.getMetabolitesView().stream()
-                .filter(x -> n.getCompartmentsOf(x).contains(c))
-                .collect(BioCollection::new,BioCollection::add,BioCollection::addAll);
-        sharedContent.addAll(toShare);
-    }
-
-    protected void exchangeWithSharedCompartment(BioNetwork n, BioMetabolite m, BioCompartment sc) {
-        //todo
-    }
-
-    protected void exchangeWithSharedCompartment(BioNetwork n, BioCollection<BioMetabolite> m, BioCompartment sc) {
-        //todo
-    }
-
-    @Override
-    public void add(BioNetwork bn) {
-        networks.add(bn);
-    }
-
-    @Override
-    public abstract void merge();
-
-    @Override
-    public abstract BioNetwork build();
-
-
-    void populateMetaNetwork(BioNetwork meta){
-
-        for(BioNetwork sub : networks){
-
-            Map<BioMetabolite,BioMetabolite> metaboliteConversion = new HashMap<>();
-            Map<BioCompartment,BioCompartment> compartmentConversion = new HashMap<>();
-            Map<BioReaction,BioReaction> reactionConversion = new HashMap<>();
-            Map<BioProtein,BioProtein> proteinConversion = new HashMap<>();
-            Map<BioEnzyme,BioEnzyme> enzymeConversion = new HashMap<>();
-            Map<BioGene,BioGene> geneConversion = new HashMap<>();
-
-            for(BioMetabolite e : sub.getMetabolitesView()){
-                BioMetabolite e2 = entityFactory.copy(e,sub);
-                meta.add(e2);
-                metaboliteConversion.put(e,e2);
-            }
-            for(BioCompartment c : sub.getCompartmentsView()){
-                BioCompartment c2 = entityFactory.copy(c,sub);
-                meta.add(c2);
-                compartmentConversion.put(c,c2);
-                c.getComponentsView().stream().
-                        filter((e) -> e.getClass().equals(BioMetabolite.class)).
-                        forEach((m) -> {
-                            BioMetabolite newMetabolite = metaboliteConversion.get(m);
-                            meta.affectToCompartment(c, newMetabolite);
-                        });
-            }
-
-            // Copy genes
-            if (keepGPR) {
-                //TODO case shared Genome
-                for (BioGene gene : sub.getGenesView()) {
-                    BioGene newGene = entityFactory.copy(gene,sub);
-                    meta.add(newGene);
-                    geneConversion.put(gene,newGene);
-                }
-                for (BioProtein protein : sub.getProteinsView()) {
-                    BioProtein newProtein = entityFactory.copy(protein,sub);
-                    meta.add(newProtein);
-                    proteinConversion.put(protein,newProtein);
-
-                    if (protein.getGene() != null) {
-                        BioGene newGene = geneConversion.get(protein.getGene());
-                        meta.affectGeneProduct(newProtein, newGene);
-                    }
-                }
-                for (BioEnzyme enzyme : sub.getEnzymesView()) {
-
-                    BioEnzyme newEnzyme = entityFactory.copy(enzyme,sub);
-                    meta.add(newEnzyme);
-                    enzymeConversion.put(enzyme,newEnzyme);
-
-                    BioCollection<BioEnzymeParticipant> participants = enzyme.getParticipantsView();
-
-                    for (BioEnzymeParticipant participant : participants) {
-                        Double quantity = participant.getQuantity();
-
-                        if (participant.getPhysicalEntity().getClass().equals(BioMetabolite.class)) {
-                            BioMetabolite metabolite = (BioMetabolite) participant.getPhysicalEntity();
-                            BioMetabolite newMetabolite = metaboliteConversion.get(metabolite);
-                            meta.affectSubUnit(newEnzyme, quantity, newMetabolite);
-                        } else if (participant.getPhysicalEntity().getClass().equals(BioProtein.class)) {
-                            BioProtein protein = (BioProtein) participant.getPhysicalEntity();
-                            BioProtein newProtein  = proteinConversion.get(protein);
-                            meta.affectSubUnit(newEnzyme, quantity, newProtein);
-                        }
-                    }
-                }
-            }
-
-            for (BioReaction r : sub.getReactionsView()) {
-
-                BioReaction newReaction = entityFactory.copy(r,sub);;
-                newReaction.setSpontaneous(r.isSpontaneous());
-                newReaction.setReversible(r.isReversible());
-                newReaction.setEcNumber(r.getEcNumber());
-
-                meta.add(newReaction);
-
-                // Copy lefts
-                for (BioReactant reactant : r.getLeftReactantsView()) {
-                    BioMetabolite newMetabolite = metaboliteConversion.get(reactant.getMetabolite());
-                    BioCompartment newCpt = compartmentConversion.get(reactant.getLocation().getId());
-                    Double sto = reactant.getQuantity();
-                    meta.affectLeft(newReaction, sto, newCpt, newMetabolite);
-                }
-
-                // Copy rights
-                for (BioReactant reactant : r.getRightReactantsView()) {
-                    BioMetabolite newMetabolite = metaboliteConversion.get(reactant.getMetabolite());
-                    BioCompartment newCpt = compartmentConversion.get(reactant.getLocation().getId());
-                    Double sto = reactant.getQuantity();
-                    meta.affectRight(newReaction, sto, newCpt, newMetabolite);
-                }
-
-                // Copy enzymes
-                if (keepGPR) {
-                    for (BioEnzyme enzyme : r.getEnzymesView()) {
-                        BioEnzyme newEnzyme = enzymeConversion.get(enzyme);
-                        meta.affectEnzyme(newReaction, newEnzyme);
-                    }
-                }
-            }
-
-            for (BioPathway pathway : sub.getPathwaysView()) {
-                BioPathway newPathway = entityFactory.copy(pathway,sub);
-                meta.add(newPathway);
-                // Add reactions into pathway
-                BioCollection<BioReaction> reactions = sub.getReactionsFromPathways(pathway);
-
-                for (BioReaction reaction : reactions) {
-                    BioReaction newReaction = reactionConversion.get(reaction);
-                    meta.affectToPathway(newPathway, newReaction);
-                }
-            }
-        }
-    }
-
-
-    void initSharedComp(BioNetwork meta){
-        for(Map.Entry<BioCompartment, Map<BioNetwork, BioCollection<BioMetabolite>>> compDescriptor : metaCompComposition.entrySet()){
-            meta.addCompartment(compDescriptor.getKey());
-            for(Map.Entry<BioNetwork, BioCollection<BioMetabolite>> content : compDescriptor.getValue().entrySet()){
-                meta.affectToCompartment(compDescriptor.getKey(),content.getValue());
-            }
-        }
-    }
-}
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/CommunityNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/CommunityNetworkBuilder.java
new file mode 100644
index 000000000..c14270c6d
--- /dev/null
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/CommunityNetworkBuilder.java
@@ -0,0 +1,49 @@
+package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
+
+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 java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+public class CommunityNetworkBuilder extends MetaNetworkBuilder implements MultiNetworkBuilder{
+
+    private BioCompartment medium;
+
+    public CommunityNetworkBuilder(){
+        setMedium();
+    }
+
+    public CommunityNetworkBuilder(BioCompartment medium){
+        this.medium=medium;
+        this.addNewSharedCompartment(medium);
+    }
+
+    public CommunityNetworkBuilder(Set<BioNetwork> networks){
+        setMedium();
+        Map<BioNetwork,String> prefixes = networks.stream()
+                .collect(Collectors.toMap(n->n,n->n.getId()+"_"));
+        this.setEntityFactory(new PrefixedMetaEntityFactory(prefixes,"pool"));
+        for(BioNetwork bn : networks){
+            this.add(bn);
+        }
+    }
+
+    private void setMedium(){
+        this.medium = new BioCompartment(UUID.randomUUID().toString(),"medium");
+        this.addNewSharedCompartment(medium);
+    }
+
+    public void exchangeWithMedium(BioNetwork sourceNetwork, BioCompartment sourceCompartment, BioMetabolite metabolite){
+        this.exchangeWithSharedCompartment(sourceNetwork, sourceCompartment, metabolite, medium);
+    };
+
+    public void add(BioNetwork bn, BioCompartment externalComp) {
+        super.add(bn);
+        this.fuseCompartmentIntoSharedCompartment(bn,externalComp,medium);
+    }
+
+}
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/DefaultMetaEntityFactory.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/DefaultMetaEntityFactory.java
deleted file mode 100644
index ce5227046..000000000
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/DefaultMetaEntityFactory.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
-
-import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioEntity;
-import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork;
-import lombok.SneakyThrows;
-
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-
-public class DefaultMetaEntityFactory implements MetaEntityFactory {
-
-    public BiFunction<BioEntity, BioNetwork, String> idSupplier = (e,bn) -> {
-        return e.getId()+"_"+bn.getId();
-    };
-
-    public BiFunction<BioEntity, BioNetwork, String> getIdSupplier() {
-        return idSupplier;
-    }
-
-    public void setIdSupplier(BiFunction<BioEntity, BioNetwork, String> idSupplier) {
-        this.idSupplier = idSupplier;
-    }
-
-    @SneakyThrows
-    @Override
-    public <E extends BioEntity> E copy(E entity, BioNetwork source) {
-        return newEntityInstance(entity, this.idSupplier.apply(entity,source));
-    }
-
-    public static <E extends BioEntity> E newEntityInstance(E entity, String newId) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
-        E newEntity = (E) entity.getClass().getDeclaredConstructor(String.class).newInstance(newId);
-        newEntity.setName(entity.getName());
-        newEntity.setSynonyms(new ArrayList<>(entity.getSynonyms()));
-        newEntity.setComment(entity.getComment());
-        newEntity.setRefs(new HashMap<>(entity.getRefs()));
-        newEntity.setAttributes(new HashMap<>(entity.getAttributes()));
-        return newEntity;
-    }
-}
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaBioMetabolite.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaBioMetabolite.java
new file mode 100644
index 000000000..4d3bfce22
--- /dev/null
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaBioMetabolite.java
@@ -0,0 +1,16 @@
+package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
+
+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;
+
+public record MetaBioMetabolite(BioMetabolite metabolite, BioCompartment sourceCompartment, BioNetwork sourceNetwork) {
+    public MetaBioMetabolite(BioMetabolite metabolite, BioCompartment sourceCompartment, BioNetwork sourceNetwork){
+        if(!sourceNetwork.containsMetabolite(metabolite.getId())) throw new IllegalArgumentException("Source network does not contains metabolites");
+        if(!sourceNetwork.containsCompartment(sourceCompartment.getId())) throw new IllegalArgumentException("Source network does not contains source compartment");
+        if(sourceCompartment.getComponentsView().get(metabolite.getId())==null) throw new IllegalArgumentException("Source compartment does not contains metabolites");
+        this.metabolite = metabolite;
+        this.sourceCompartment = sourceCompartment;
+        this.sourceNetwork = sourceNetwork;
+    }
+}
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaEntityFactory.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaEntityFactory.java
index 554d7b182..c7254a7cf 100644
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaEntityFactory.java
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaEntityFactory.java
@@ -1,10 +1,16 @@
 package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
 
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioCompartment;
 import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioEntity;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioMetabolite;
 import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork;
 
+import java.util.Collection;
+
 public interface MetaEntityFactory {
 
-    public <E extends BioEntity> E copy(E entity, BioNetwork source);
+    <E extends BioEntity> E createMetaEntity(E originalEntity, BioNetwork source);
+    BioMetabolite createSharedCompound(BioMetabolite originalEntity, BioCompartment sharedComp);
+    BioMetabolite createPoolCompound (Collection<BioMetabolite> entities, BioCompartment sharedComp);
 
 }
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
index 7af6a2b5f..b5b5b2958 100644
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
@@ -1,45 +1,316 @@
 package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
 
-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.*;
 import fr.inrae.toulouse.metexplore.met4j_core.biodata.collection.BioCollection;
+import lombok.Getter;
+import lombok.Setter;
 
-import java.util.UUID;
+import java.util.*;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
-public class MetaNetworkBuilder extends AbstractMetaNetworkBuilder implements MultiNetworkBuilder{
+/**
+ * A class that creates a meta-networks from multiple sub-networks. A meta-network is a single model which contains several
+ * sub-networks that remains individualized within the meta-network (as opposed to models fusion), but which can share
+ * some of their components with other sub-networks through shared compartments.
+ */
+public class MetaNetworkBuilder implements MultiNetworkBuilder{
 
+    private Map<BioMetabolite,BioMetabolite> metaboliteConversion;
+    private Map<BioCompartment,BioCompartment> compartmentConversion;
+    private Map<BioReaction,BioReaction> reactionConversion;
+    private Map<BioProtein,BioProtein> proteinConversion;
+    private Map<BioEnzyme,BioEnzyme> enzymeConversion;
+    private Map<BioGene,BioGene> geneConversion;
 
-    private BioCompartment medium;
+    protected BioCollection<BioNetwork> networks = new BioCollection<>();
+    private Map<BioCompartment, Set<MetaBioMetabolite>> metaCompComposition = new HashMap<>();
 
-    public MetaNetworkBuilder(BioNetwork superNetwork){
-        setMedium();
+    @Setter
+    protected Function<BioMetabolite,String> getSharedIdFunction = BioEntity::getName;
+    @Setter
+    private String poolReactionPrefix = "poolReaction_";
+    @Setter
+    private String sharedTransportPrefix = "transport_";
+    @Setter
+    protected BiFunction<BioMetabolite, BioMetabolite, BioReaction> createLinkWithSharedPool = (m, pool) -> {
+        BioReaction r=new BioReaction(poolReactionPrefix+ m.getId());
+        r.setReversible(true);
+        return r;
+    };
+    @Setter
+    protected BiFunction<BioMetabolite, BioCompartment, BioReaction> createTransportWithSharedComp = (m, comp) -> {
+        BioReaction r=new BioReaction(sharedTransportPrefix+ m.getId()+"_to_"+comp.getId());
+        r.setReversible(true);
+        return r;
+    };
+    @Setter
+    @Getter
+    protected MetaEntityFactory entityFactory;
+    protected HashMap<BioCompartment,BioCompartment> fuseMap= new HashMap<>();
+    protected Boolean keepGPR = false;
+
+    /**
+     * enable a compartment to be shared between multiple organisms
+     * @param sc
+     */
+    public void addNewSharedCompartment(BioCompartment sc) {
+        metaCompComposition.put(sc,new HashSet<>());
+    }
+
+    /**
+     * Fuse a sub-network's compartment into a meta-network's shared compartment. The former will be replaced by the latter during build.
+     * All compartment's component will be added to the shared compartment
+     * @param n
+     * @param c
+     * @param sc
+     */
+    public void fuseCompartmentIntoSharedCompartment(BioNetwork n, BioCompartment c, BioCompartment sc) {
+        if(!metaCompComposition.containsKey(sc)) throw new IllegalArgumentException("Shared meta-compartment "+sc.getId()+" not found in network");
+        fuseMap.put(c,sc);
     }
 
-    private void setMedium(){
-        this.medium = new BioCompartment(UUID.randomUUID().toString(),"medium");
-        this.addSharedCompartment(medium);
+    /**
+     * Bump a sub-network's compartment into a meta-network's shared compartment. For each compartment's component,
+     * a copy is created within the shared compartment
+     * @param n
+     * @param c
+     * @param sc
+     */
+    public void bumpCompartmentIntoSharedCompartment(BioNetwork n, BioCompartment c, BioCompartment sc) {
+        BioCollection<BioMetabolite> toShare = n.getMetabolitesView().stream()
+                .filter(x -> n.getCompartmentsOf(x).contains(c))
+                .collect(BioCollection::new,BioCollection::add,BioCollection::addAll);
+        this.exchangeWithSharedCompartment(n,c,toShare,sc);
+    }
+
+    /**
+     *
+     * @param sourceNetwork the original subnetwork of the compound
+     * @param sourceCompartment  the original compartment of the compound in the subnetwork
+     * @param metabolite the compound
+     * @param sc the target shared meta-compartment that will receive the compound
+     */
+    public void exchangeWithSharedCompartment(BioNetwork sourceNetwork, BioCompartment sourceCompartment, BioMetabolite metabolite, BioCompartment sc) {
+        if(!metaCompComposition.containsKey(sc)) throw new IllegalArgumentException("Shared meta-compartment "+sc.getId()+" not found in network");
+        metaCompComposition.get(sc).add(new MetaBioMetabolite(metabolite,sourceCompartment,sourceNetwork));
+    }
+
+    /**
+     *
+     * @param sourceNetwork the original subnetwork of the compounds
+     * @param sourceCompartment  the original compartment of the compounds in the subnetwork
+     * @param metabolites the compounds
+     * @param sc the target shared meta-compartment that will receive the compounds
+     */
+    public void exchangeWithSharedCompartment(BioNetwork sourceNetwork, BioCompartment sourceCompartment, BioCollection<BioMetabolite> metabolites, BioCompartment sc) {
+        if(!metaCompComposition.containsKey(sc)) throw new IllegalArgumentException("Shared meta-compartment "+sc.getId()+" not found in network");
+        Set<MetaBioMetabolite> scCompo = metaCompComposition.get(sc);
+        for(BioMetabolite m : metabolites ){
+            scCompo.add(new MetaBioMetabolite(m,sourceCompartment,sourceNetwork));
+        }
     }
 
     @Override
     public void add(BioNetwork bn) {
-        super.add(bn);
-        this.addSharedCompartment(medium,this.networks.stream().toArray(BioNetwork[]::new));
+        networks.add(bn);
     }
 
-    @Override
-    public void merge() {
-        //todo
+    protected BioNetwork initMetaNetwork(){
+        this.metaboliteConversion = new HashMap<>();
+        this.compartmentConversion = new HashMap<>();
+        this.reactionConversion = new HashMap<>();
+        this.proteinConversion = new HashMap<>();
+        this.enzymeConversion = new HashMap<>();
+        this.geneConversion = new HashMap<>();
+        return new BioNetwork();
     }
 
-    public void exchangeWithMedium(BioNetwork n, BioMetabolite m){
-        this.exchangeWithSharedCompartment(n, m, medium);
-    };
+    protected void populateMetaNetwork(BioNetwork meta){
+
+        for(BioNetwork sub : networks){
+
+            for(BioMetabolite e : sub.getMetabolitesView()){
+                BioMetabolite e2 = entityFactory.createMetaEntity(e,sub);
+                meta.add(e2);
+                metaboliteConversion.put(e,e2);
+            }
+            for(BioCompartment c : sub.getCompartmentsView()){
+                BioCompartment c2 = fuseMap.get(c);
+                if(c2==null){
+                    c2 = entityFactory.createMetaEntity(c,sub);
+                    meta.add(c2);
+                }
+                compartmentConversion.put(c,c2);
+                BioCollection<BioMetabolite> content = c.getComponentsView().stream()
+                        .filter((e) -> e.getClass().equals(BioMetabolite.class))
+                        .map(metaboliteConversion::get)
+                        .collect(BioCollection::new,BioCollection::add,BioCollection::addAll);
+
+                meta.affectToCompartment(c2, content);
+            }
+
+            // Copy genes
+            if (keepGPR) {
+                //TODO case shared Genome
+                for (BioGene gene : sub.getGenesView()) {
+                    BioGene newGene = entityFactory.createMetaEntity(gene,sub);
+                    meta.add(newGene);
+                    geneConversion.put(gene,newGene);
+                }
+                for (BioProtein protein : sub.getProteinsView()) {
+                    BioProtein newProtein = entityFactory.createMetaEntity(protein,sub);
+                    meta.add(newProtein);
+                    proteinConversion.put(protein,newProtein);
+
+                    if (protein.getGene() != null) {
+                        BioGene newGene = geneConversion.get(protein.getGene());
+                        meta.affectGeneProduct(newProtein, newGene);
+                    }
+                }
+                for (BioEnzyme enzyme : sub.getEnzymesView()) {
+
+                    BioEnzyme newEnzyme = entityFactory.createMetaEntity(enzyme,sub);
+                    meta.add(newEnzyme);
+                    enzymeConversion.put(enzyme,newEnzyme);
+
+                    BioCollection<BioEnzymeParticipant> participants = enzyme.getParticipantsView();
+
+                    for (BioEnzymeParticipant participant : participants) {
+                        Double quantity = participant.getQuantity();
+
+                        if (participant.getPhysicalEntity().getClass().equals(BioMetabolite.class)) {
+                            BioMetabolite metabolite = (BioMetabolite) participant.getPhysicalEntity();
+                            BioMetabolite newMetabolite = metaboliteConversion.get(metabolite);
+                            meta.affectSubUnit(newEnzyme, quantity, newMetabolite);
+                        } else if (participant.getPhysicalEntity().getClass().equals(BioProtein.class)) {
+                            BioProtein protein = (BioProtein) participant.getPhysicalEntity();
+                            BioProtein newProtein  = proteinConversion.get(protein);
+                            meta.affectSubUnit(newEnzyme, quantity, newProtein);
+                        }
+                    }
+                }
+            }
+
+            for (BioReaction r : sub.getReactionsView()) {
+
+                BioReaction newReaction = entityFactory.createMetaEntity(r,sub);
+                newReaction.setSpontaneous(r.isSpontaneous());
+                newReaction.setReversible(r.isReversible());
+                newReaction.setEcNumber(r.getEcNumber());
+
+                meta.add(newReaction);
+                reactionConversion.put(r,newReaction);
+
+                // Copy lefts
+                for (BioReactant reactant : r.getLeftReactantsView()) {
+                    BioMetabolite newMetabolite = metaboliteConversion.get(reactant.getMetabolite());
+                    BioCompartment newCpt = compartmentConversion.get(reactant.getLocation());
+                    Double sto = reactant.getQuantity();
+                    meta.affectLeft(newReaction, sto, newCpt, newMetabolite);
+                }
+
+                // Copy rights
+                for (BioReactant reactant : r.getRightReactantsView()) {
+                    BioMetabolite newMetabolite = metaboliteConversion.get(reactant.getMetabolite());
+                    BioCompartment newCpt = compartmentConversion.get(reactant.getLocation());
+                    Double sto = reactant.getQuantity();
+                    meta.affectRight(newReaction, sto, newCpt, newMetabolite);
+                }
+
+                // Copy enzymes
+                if (keepGPR) {
+                    for (BioEnzyme enzyme : r.getEnzymesView()) {
+                        BioEnzyme newEnzyme = enzymeConversion.get(enzyme);
+                        meta.affectEnzyme(newReaction, newEnzyme);
+                    }
+                }
+            }
+
+            for (BioPathway pathway : sub.getPathwaysView()) {
+                BioPathway newPathway = entityFactory.createMetaEntity(pathway,sub);
+                meta.add(newPathway);
+                // Add reactions into pathway
+                BioCollection<BioReaction> reactions = sub.getReactionsFromPathways(pathway);
+
+                for (BioReaction reaction : reactions) {
+                    BioReaction newReaction = reactionConversion.get(reaction);
+                    meta.affectToPathway(newPathway, newReaction);
+                }
+            }
+        }
+    }
+
+    public void setAliasPrefixes(Map<BioNetwork,String> aliases, String poolPrefix){
+        this.setEntityFactory(new PrefixedMetaEntityFactory(aliases, poolPrefix));
+    }
 
     @Override
     public BioNetwork build() {
-        //todo
-        return null;
+        BioNetwork meta = this.initMetaNetwork();
+        //create shared compartment
+        this.initSharedComp(meta);
+        //add each subnetwork content
+        this.populateMetaNetwork(meta);
+        //add shared compartment components and exchanges
+        this.populateSharedComp(meta);
+        //link compounds in shared compartment
+        this.linkCompoundsInSharedComp(meta);
+        return meta;
+    }
+    protected void initSharedComp(BioNetwork meta) {
+        for (Map.Entry<BioCompartment, Set<MetaBioMetabolite>> compDescriptor : metaCompComposition.entrySet()) {
+            BioCompartment sc = compDescriptor.getKey();
+            meta.addCompartment(sc);
+        }
     }
 
+    protected void populateSharedComp(BioNetwork meta){
+        for(Map.Entry<BioCompartment, Set<MetaBioMetabolite>> compDescriptor : metaCompComposition.entrySet()){
+            for(MetaBioMetabolite m : compDescriptor.getValue()){
+                BioMetabolite m1 = metaboliteConversion.get(m.metabolite());
+                BioMetabolite m2 = entityFactory.createSharedCompound(m1,compDescriptor.getKey());
+                meta.add(m2);
+                meta.affectToCompartment(compDescriptor.getKey(),m2);
+                BioReaction t = createTransportWithSharedComp.apply(m1,compDescriptor.getKey());
+                meta.add(t);
+                meta.affectLeft(t,1.0,compartmentConversion.get(m.sourceCompartment()),metaboliteConversion.get(m.metabolite()));
+                meta.affectLeft(t,1.0,compDescriptor.getKey(),m2);
+            }
+        }
+    }
+
+    /**
+     * Links the source-specific metabolites representing the same compounds in the shared compartments, by creating a shared "pool" entity
+     * alongside their interconversion reactions.
+     * @param meta
+     */
+    protected void linkCompoundsInSharedComp(BioNetwork meta) {
+        //loop over each shared compartment
+        for(BioCompartment sharedComp : metaCompComposition.keySet()){
+            //retrieve all metabolite components that has been previously set, using whole compartment fusing or individual additions
+            BioCollection<BioMetabolite> content = sharedComp.getComponentsView().stream()
+                    .filter((e) -> e.getClass().equals(BioMetabolite.class))
+                    .map(o -> (BioMetabolite)o)
+                    .collect(BioCollection::new,BioCollection::add,BioCollection::addAll);
+
+            //all entities that represent the same compounds from different source are grouped
+            Map<String, List<BioMetabolite>> compoundGroups = content.stream().collect(Collectors.groupingBy(getSharedIdFunction));
+
+            //for each group, create a "pool" metabolite that represent them
+            for(Map.Entry<String, List<BioMetabolite>> group : compoundGroups.entrySet()){
+                BioMetabolite pool = this.entityFactory.createPoolCompound(group.getValue(),sharedComp);
+                meta.add(pool);
+                meta.affectToCompartment(sharedComp,pool);
+                //for each member of the group, create a reversible reaction linking them to their "pool" counterpart
+                for(BioMetabolite e : group.getValue()){
+                    BioReaction r = this.createLinkWithSharedPool.apply(e,pool);
+                    meta.add(r);
+                    meta.affectRight(r,1.0, sharedComp,e);
+                    meta.affectLeft(r,1.0, sharedComp,pool);
+                }
+            }
+        }
+    }
 }
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiNetworkBuilder.java
index c17e2cadc..12f084efa 100644
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiNetworkBuilder.java
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiNetworkBuilder.java
@@ -10,12 +10,11 @@ public interface MultiNetworkBuilder {
      */
     public void add(BioNetwork bn);
 
-    public void merge();
-
     /**
      * create a new network which encompass all added networks
      * @return
      */
     public BioNetwork build();
 
+
 }
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiOrganNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiOrganNetworkBuilder.java
deleted file mode 100644
index ae4c1b699..000000000
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MultiOrganNetworkBuilder.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
-
-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.collection.BioCollection;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-public class MultiOrganNetworkBuilder extends AbstractMetaNetworkBuilder implements MultiNetworkBuilder{
-
-    @Override
-    public void merge() {
-        //TODO
-    }
-
-
-    @Override
-    public void addSharedCompartment(BioCompartment sc, BioNetwork... bn) {
-        super.addSharedCompartment(sc,bn);
-    }
-
-    @Override
-    public void fuseCompartmentIntoSharedCompartment(BioNetwork n, BioCompartment c, BioCompartment sc) {
-        super.fuseCompartmentIntoSharedCompartment(n,c,sc);
-    }
-
-    @Override
-    public void bumpCompartmentIntoSharedCompartment(BioNetwork n, BioCompartment c, BioCompartment sc) {
-        super.bumpCompartmentIntoSharedCompartment(n,c,sc);
-    }
-
-    @Override
-    public void exchangeWithSharedCompartment(BioNetwork n, BioMetabolite m, BioCompartment sc) {
-        super.exchangeWithSharedCompartment(n,m,sc);
-    }
-
-    @Override
-    public void exchangeWithSharedCompartment(BioNetwork n, BioCollection<BioMetabolite> m, BioCompartment sc) {
-        super.exchangeWithSharedCompartment(n,m,sc);
-    }
-
-    @Override
-    public BioNetwork build() {
-        //todo
-        //create empty meta network
-        BioNetwork meta = new BioNetwork();
-        //add each subnetwork content in dedicated compartment
-        this.populateMetaNetwork(meta);
-        //create shared compartment and the exchanged content
-        this.initSharedComp(meta);
-        return null;
-    }
-}
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/NestedNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/NestedNetworkBuilder.java
deleted file mode 100644
index aeca0c100..000000000
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/NestedNetworkBuilder.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
-
-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;
-
-public class NestedNetworkBuilder extends AbstractMetaNetworkBuilder implements MultiNetworkBuilder{
-
-    private BioNetwork superNet;
-    private BioCompartment superNetComp;
-
-    public NestedNetworkBuilder(BioNetwork superNetwork){
-        setSuperNetwork(superNetwork);
-    }
-
-    private void setSuperNetwork(BioNetwork bn){
-        superNet=bn;
-        this.superNetComp = new BioCompartment(bn.getId());
-        this.addSharedCompartment(superNetComp,superNet);
-    }
-
-    @Override
-    public void add(BioNetwork bn) {
-        super.add(bn);
-        this.addSharedCompartment(superNetComp,this.networks.stream().toArray(BioNetwork[]::new));
-    }
-
-    @Override
-    public void merge() {
-        //todo
-    }
-
-    public void exchangeWithSuperNetwork(BioNetwork n, BioMetabolite m){
-        this.exchangeWithSharedCompartment(n, m, superNetComp);
-    };
-
-    @Override
-    public BioNetwork build() {
-        //todo
-        return null;
-    }
-
-}
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PanNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PanNetworkBuilder.java
deleted file mode 100644
index 346f71cbf..000000000
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PanNetworkBuilder.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
-
-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.collection.BioCollection;
-
-public class PanNetworkBuilder implements MultiNetworkBuilder {
-    @Override
-    public void add(BioNetwork bn) {
-        //todo
-    }
-
-    @Override
-    public void merge() {
-        //todo
-    }
-
-    @Override
-    public BioNetwork build() {
-        //todo
-        return null;
-    }
-}
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java
new file mode 100644
index 000000000..3137021eb
--- /dev/null
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java
@@ -0,0 +1,86 @@
+package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
+
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioCompartment;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioEntity;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioMetabolite;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork;
+import lombok.SneakyThrows;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+public class PrefixedMetaEntityFactory implements MetaEntityFactory {
+
+    Map<BioNetwork,String> sourcePrefixMap;
+    String poolPrefix;
+    
+    public PrefixedMetaEntityFactory(Map<BioNetwork,String> sourcePrefixMap, String poolPrefix){
+        this.sourcePrefixMap=sourcePrefixMap;
+        this.poolPrefix=poolPrefix;
+    }
+
+    /**
+     * create prefix-id from network alias
+     */
+    public BiFunction<String, BioNetwork, String> addSourcePrefix = (id, bn) -> sourcePrefixMap.get(bn)+"_"+id;
+
+    /**
+     * create sharedCompartment-suffix id
+     */
+    public BiFunction<String, BioCompartment, String> addCompSuffix = (id, comp) -> id+"_"+comp.getId();
+    public BiFunction<String, BioCompartment, String> addCompPreffix = (id, comp) -> comp.getId()+"_"+id;
+
+
+    /**
+     * remove prefix-id from network alias
+     */
+    public Function<String, String> removeSourceSuffix = id -> {
+            for(String s : sourcePrefixMap.values()) {
+                id = id.replaceAll("^" + s + "_","");
+            };
+            return id;
+        };
+
+    /**
+     * create pool-prefix id
+     */
+    public Function<String, String> addPoolFlag = (id) -> poolPrefix+"_"+id;
+
+
+    @SneakyThrows
+    @Override
+    public <E extends BioEntity> E createMetaEntity(E originalEntity, BioNetwork source) {
+        return newEntityInstance(originalEntity, this.addSourcePrefix.apply(originalEntity.getId(),source));
+    }
+
+    @SneakyThrows
+    @Override
+    public BioMetabolite createSharedCompound(BioMetabolite entity, BioCompartment sharedCompartment) {
+        return newEntityInstance(entity, this.addCompSuffix.apply(entity.getId(),sharedCompartment));
+    }
+
+    @SneakyThrows
+    @Override
+    public BioMetabolite createPoolCompound(Collection<BioMetabolite> entities, BioCompartment sharedCompartment) {
+        BioMetabolite entity = entities.iterator().next();
+        return newEntityInstance(entity, removeSourceSuffix
+                .andThen(s -> addCompPreffix.apply(s,sharedCompartment))
+                .andThen(addPoolFlag)
+                .apply(entity.getId()));
+    }
+
+    private static <E extends BioEntity> E newEntityInstance(E entity, String newId) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
+        E newEntity = (E) entity.getClass().getDeclaredConstructor(String.class).newInstance(newId);
+        newEntity.setName(entity.getName());
+        newEntity.setSynonyms(new ArrayList<>(entity.getSynonyms()));
+        newEntity.setComment(entity.getComment());
+        newEntity.setRefs(new HashMap<>(entity.getRefs()));
+        newEntity.setAttributes(new HashMap<>(entity.getAttributes()));
+        return newEntity;
+    }
+}
diff --git a/met4j-core/src/test/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/TestMetaNetworkBuilder.java b/met4j-core/src/test/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/TestMetaNetworkBuilder.java
new file mode 100644
index 000000000..75a02f9fb
--- /dev/null
+++ b/met4j-core/src/test/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/TestMetaNetworkBuilder.java
@@ -0,0 +1,185 @@
+package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
+
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.*;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.HashMap;
+
+import static org.junit.Assert.*;
+
+public class TestMetaNetworkBuilder {
+
+
+    public static BioNetwork bn1, bn2, bn3, bn4;
+    public static BioMetabolite a1,b1,c1,c1ex,c2,c2ex,d3, d3ex, f3, d4, d4ex;
+    public static BioCompartment co1in, co1ex, co2in, co2ex, co3in, co3ex,co4in, co4ex,medium,medium2;
+    public static BioReaction r1, r3, tc1, tc2, td3,td4;
+
+
+    public static BioNetwork initBump() {
+        HashMap<BioNetwork,String> alias = new HashMap<>();
+        alias.put(bn1,"model1");
+        alias.put(bn2,"model2");
+        alias.put(bn3,"model3");
+        alias.put(bn4,"model4");
+        medium=new BioCompartment("medium");
+        medium2=new BioCompartment("medium2");
+        CommunityNetworkBuilder builder = new CommunityNetworkBuilder(medium);
+        builder.addNewSharedCompartment(medium2);
+        builder.setEntityFactory(new PrefixedMetaEntityFactory(alias,"pool"));
+        builder.add(bn1);
+        builder.add(bn2);
+        builder.add(bn3);
+        builder.add(bn4);
+        builder.bumpCompartmentIntoSharedCompartment(bn1,co1ex,medium);
+        builder.bumpCompartmentIntoSharedCompartment(bn2,co2ex,medium);
+        builder.bumpCompartmentIntoSharedCompartment(bn3,co3ex,medium);
+        builder.bumpCompartmentIntoSharedCompartment(bn4,co4ex,medium2);
+        builder.bumpCompartmentIntoSharedCompartment(bn4,co4ex,medium);
+        return builder.build();
+    }
+
+    public static BioNetwork initFuse() {
+        HashMap<BioNetwork,String> alias = new HashMap<>();
+        alias.put(bn1,"model1");
+        alias.put(bn2,"model2");
+        alias.put(bn3,"model3");
+        alias.put(bn4,"model4");
+        medium=new BioCompartment("medium");
+        medium2=new BioCompartment("medium2");
+        CommunityNetworkBuilder builder = new CommunityNetworkBuilder(medium);
+        builder.addNewSharedCompartment(medium2);
+        builder.setEntityFactory(new PrefixedMetaEntityFactory(alias,"pool"));
+        builder.add(bn1,co1ex);
+        builder.add(bn2,co2ex);
+        builder.add(bn3,co3ex);
+        builder.add(bn4);
+        builder.fuseCompartmentIntoSharedCompartment(bn4,co4ex,medium2);
+        return builder.build();
+    }
+
+    @BeforeClass
+    public static void beforeClass() {
+        bn1 = new BioNetwork("bn1");
+        bn2 = new BioNetwork("bn2");
+        bn3 = new BioNetwork("bn3");
+        bn4 = new BioNetwork("bn4");
+        a1 = new BioMetabolite("a_in","a");
+        b1 = new BioMetabolite("b_in","b");
+        c1 = new BioMetabolite("c_in","c");
+        c1ex = new BioMetabolite("c_ex","c");
+        r1 = new BioReaction("r1");
+        tc1 = new BioReaction("tc");
+        co1in = new BioCompartment("in");
+        co1ex = new BioCompartment("ex");
+        bn1.add(a1,b1,c1,c1ex,r1,tc1,co1in,co1ex);
+        bn1.affectToCompartment(co1in,a1,b1,c1);
+        bn1.affectToCompartment(co1ex,c1ex);
+        bn1.affectLeft(r1,1.0,co1in,a1);
+        bn1.affectLeft(r1,1.0,co1in,b1);
+        bn1.affectRight(r1,2.0,co1in,c1);
+        bn1.affectLeft(tc1,1.0,co1in,c1);
+        bn1.affectRight(tc1,1.0,co1ex,c1ex);
+        c2 = new BioMetabolite("c_in","c");
+        c2ex = new BioMetabolite("c_ex","c");
+        tc2 = new BioReaction("tc");
+        co2in = new BioCompartment("in");
+        co2ex = new BioCompartment("ex");
+        bn2.add(c2,c2ex,tc2,co2in,co2ex);
+        bn2.affectToCompartment(co2in,c2);
+        bn2.affectToCompartment(co2ex,c2ex);
+        bn2.affectLeft(tc2,1.0,co2in,c2);
+        bn2.affectRight(tc2,1.0,co2ex,c2ex);
+        d3ex = new BioMetabolite("d_ex","d");
+        d3 = new BioMetabolite("d_in","d");
+        f3 = new BioMetabolite("f_in","f");
+        r3 = new BioReaction("r2");
+        td3 = new BioReaction("td");
+        co3in = new BioCompartment("in");
+        co3ex = new BioCompartment("ex");
+        bn3.add(d3ex,d3,f3,r3,td3,co3in,co3ex);
+        bn3.affectToCompartment(co3in,d3,f3);
+        bn3.affectToCompartment(co3ex,d3ex);
+        bn3.affectLeft(td3,1.0,co3in,d3);
+        bn3.affectRight(td3,1.0,co3ex,d3ex);
+        bn3.affectLeft(r3,1.0,co3in,d3);
+        bn3.affectRight(r3,1.0,co3in,f3);
+        d4 = new BioMetabolite("d_in","d");
+        d4ex = new BioMetabolite("d_ex","d");
+        td4 = new BioReaction("td");
+        co4in = new BioCompartment("in");
+        co4ex = new BioCompartment("ex");
+        bn4.add(d4,d4ex,td4,co4in,co4ex);
+        bn4.affectToCompartment(co4in,d4);
+        bn4.affectToCompartment(co4ex,d4ex);
+        bn4.affectLeft(td4,1.0,co4in,d4);
+        bn4.affectRight(td4,1.0,co4ex,d4ex);
+    }
+
+    @Test
+    public void testCompoundsFuse(){
+        BioNetwork meta = initFuse();
+        assertEquals(14,meta.getMetabolitesView().size());
+        assertNotNull(meta.getMetabolite("model1_a_in"));
+        assertNotNull(meta.getMetabolite("model1_c_in"));
+        assertNotNull(meta.getMetabolite("model1_c_ex"));
+        assertNotNull(meta.getMetabolite("model2_c_ex"));
+        assertNotNull(meta.getMetabolite("model2_c_in"));
+
+        assertNotNull(meta.getMetabolite("pool_medium_c_ex"));
+        assertNotNull(meta.getMetabolite("pool_medium_d_ex"));
+        assertNotNull(meta.getMetabolite("pool_medium2_d_ex"));
+    }
+
+    @Test
+    public void testReactionsFuse() {
+        BioNetwork meta = initFuse();
+        assertEquals(10, meta.getReactionsView().size());
+        assertNotNull(meta.getReaction("poolReaction_model2_c_ex"));
+        assertNotNull(meta.getReaction("poolReaction_model1_c_ex"));
+        assertNotNull(meta.getReaction("poolReaction_model3_d_ex"));
+    }
+
+    @Test
+    public void testCompartmentFuse(){
+        BioNetwork meta = initFuse();
+        assertEquals(6,meta.getCompartmentsView().size());
+    }
+
+
+    @Test
+    public void testCompoundsBump(){
+        BioNetwork meta = initBump();
+        assertEquals(19,meta.getMetabolitesView().size());
+        assertNotNull(meta.getMetabolite("model1_a_in"));
+        assertNotNull(meta.getMetabolite("model1_c_in"));
+        assertNotNull(meta.getMetabolite("model1_c_ex"));
+        assertNotNull(meta.getMetabolite("model2_c_ex"));
+        assertNotNull(meta.getMetabolite("model2_c_in"));
+
+        assertNotNull(meta.getMetabolite("pool_medium_c_ex_medium"));
+        assertNotNull(meta.getMetabolite("pool_medium_d_ex_medium"));
+        assertNotNull(meta.getMetabolite("pool_medium2_d_ex_medium2"));
+    }
+
+    @Test
+    public void testReactionsBump() {
+        BioNetwork meta = initBump();
+        assertEquals(16, meta.getReactionsView().size());
+        assertNotNull(meta.getReaction("poolReaction_model2_c_ex_medium"));
+        assertNotNull(meta.getReaction("poolReaction_model1_c_ex_medium"));
+        assertNotNull(meta.getReaction("poolReaction_model3_d_ex_medium"));
+        assertNotNull(meta.getReaction("transport_model1_c_ex_to_medium"));
+        assertNotNull(meta.getReaction("poolReaction_model4_d_ex_medium2"));
+        assertNotNull(meta.getReaction("poolReaction_model4_d_ex_medium"));
+        assertNotNull(meta.getReaction("transport_model4_d_ex_to_medium2"));
+        assertNotNull(meta.getReaction("transport_model4_d_ex_to_medium"));
+    }
+
+    @Test
+    public void testCompartmentBump(){
+        BioNetwork meta = initBump();
+        assertEquals(10,meta.getCompartmentsView().size());
+    }
+}
-- 
GitLab


From 168bf2c6f61555711a38857a48649133bea7df2c Mon Sep 17 00:00:00 2001
From: cfrainay <clement.frainay@inrae.fr>
Date: Wed, 18 Sep 2024 00:03:18 +0200
Subject: [PATCH 03/17] app stub

---
 .../reconstruction/CreateMetaNetwork.java     | 173 ++++++++++++++++++
 1 file changed, 173 insertions(+)
 create mode 100644 met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java

diff --git a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
new file mode 100644
index 000000000..d5767c203
--- /dev/null
+++ b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
@@ -0,0 +1,173 @@
+package fr.inrae.toulouse.metexplore.met4j_toolbox.reconstruction;
+
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.*;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork.CommunityNetworkBuilder;
+import fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork.PrefixedMetaEntityFactory;
+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_io.jsbml.writer.JsbmlWriter;
+import fr.inrae.toulouse.metexplore.met4j_io.jsbml.writer.Met4jSbmlWriterException;
+import fr.inrae.toulouse.metexplore.met4j_toolbox.generic.AbstractMet4jApplication;
+import fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.EnumFormats;
+import fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.EnumParameterTypes;
+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.util.HashMap;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.function.Function;
+
+import static fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.EnumFormats.Sbml;
+import static fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.EnumParameterTypes.InputFile;
+import static fr.inrae.toulouse.metexplore.met4j_toolbox.reconstruction.CreateMetaNetwork.strategy.*;
+
+public class CreateMetaNetwork extends AbstractMet4jApplication {
+
+    //arguments
+    @Format(name = Sbml)
+    @ParameterType(name = InputFile)
+    @Option(name = "-n1", aliases = {"--network1"},  usage = "input SBML file: path to first network, in sbml format.", required = true)
+    public String sbml1FilePath;
+    @Option(name = "-n2", aliases = {"--network2"},  usage = "input SBML file: path to second network, in sbml format.", required = true)
+    public String sbml2FilePath;
+
+    @Option(name = "-n1ex", aliases = {"--external1"},  usage = "external compartment identifier in first network.", required = true)
+    public String external1;
+    @Option(name = "-n2ex", aliases = {"--external2"},  usage = "external compartment identifier in second network.", required = true)
+    public String external2;
+
+    @Option(name = "-n1px", aliases = {"--n1prefix"},  usage = "prefix that will be added to first network's entities identifiers", required = false)
+    public String n1prefix = "Net1_";
+    @Option(name = "-n2px", aliases = {"--n2prefix"},   usage = "prefix that will be added to second network's entities identifiers", required = false)
+    public String n2prefix = "Net2_";
+
+    @Option(name = "-k", aliases = {"--keepCompartment"}, usage = "keep the original external compartments in the meta-network, otherwise, they will be fused into the new shared external compartment", required = false)
+    public boolean keepCompartment = false;
+
+    enum strategy {by_metanetx, by_name, by_id}
+    @Option(name = "-mc", aliases = {"--mergingCriterion"}, usage = "field used to identify the same metabolites across the two different networks. " +
+            "\"by_name\"/\"by_id\" can be used if names/identifiers are consistent and unambiguous across source models, \"by_metanetx\" can be used if models contains MetaNetX identifiers in annotation field using standard miriam format.")
+    public CreateMetaNetwork.strategy mergingCriterion = by_name;
+
+    @ParameterType(name = EnumParameterTypes.OutputFile)
+    @Format(name = EnumFormats.Sbml)
+    @Option(name = "-o", usage = "output meta-network SBML file", required = true)
+    public String outputPath = null;
+
+    public static void main(String[] args) throws Met4jSbmlWriterException {
+        CreateMetaNetwork app = new CreateMetaNetwork();
+        app.parseArguments(args);
+        app.run();
+    }
+
+    public void run() throws Met4jSbmlWriterException {
+
+        if(Objects.equals(this.n1prefix, this.n2prefix)){
+            System.err.println("Error: prefixes must be different");
+            System.exit(1);
+        }
+
+        //import networks
+        System.out.print("Importing network 1...");
+        JsbmlReader reader = new JsbmlReader(this.sbml1FilePath);
+        BioNetwork network1 = null;
+        try {
+            network1 = reader.read();
+        } catch (Met4jSbmlReaderException e) {
+            System.err.println("Error while reading the first SBML file");
+            System.err.println(e.getMessage());
+            System.exit(1);
+        }
+        BioCompartment co1ex = network1.getCompartment(external1);
+        if(co1ex==null){
+            System.err.println("Error: external compartment " + external1 + " not found in network 1");
+            System.exit(1);
+        }
+        System.out.println(" Done.");
+
+
+        System.out.print("Importing network 2...");
+        reader = new JsbmlReader(this.sbml2FilePath);
+        BioNetwork network2 = null;
+        try {
+            network2 = reader.read();
+        } catch (Met4jSbmlReaderException e) {
+            System.err.println("Error while reading the second SBML file");
+            System.err.println(e.getMessage());
+            System.exit(1);
+        }
+        BioCompartment co2ex = network2.getCompartment(external2);
+        if(co2ex==null){
+            System.err.println("Error: external compartment " + external2 + " not found in network 2");
+            System.exit(1);
+        }
+        System.out.println(" Done.");
+
+        System.out.print("Creating meta-network...");
+        //setup
+        BioCompartment medium=new BioCompartment("medium"); medium.setName("medium");
+        HashMap<BioNetwork,String> alias = new HashMap<>();
+        alias.put(network1,n1prefix);alias.put(network2,n2prefix);
+        CommunityNetworkBuilder builder = new CommunityNetworkBuilder(medium);
+        builder.setEntityFactory(new PrefixedMetaEntityFactory(alias,"pool"));
+        Function<BioMetabolite, String> getSharedIdFunction;
+        switch (mergingCriterion) {
+            case by_metanetx:
+                getSharedIdFunction = x -> {
+                    try {
+                        BioRef r = x.getRefs("metanetx.chemical").iterator().next();
+                        return r.getId();
+                    } catch (NoSuchElementException|java.lang.NullPointerException e) {
+                        return "unknown_"+UUID.randomUUID();
+                    }
+                };
+                break;
+            case by_id:
+                getSharedIdFunction = BioEntity::getId;
+                break;
+            case by_name:
+                getSharedIdFunction = BioEntity::getName;
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid merging criterion: " + mergingCriterion);
+        }
+        builder.setGetSharedIdFunction(getSharedIdFunction);
+        builder.add(network1);
+        builder.add(network2);
+
+        //build meta-network
+        if(!keepCompartment){
+            builder.fuseCompartmentIntoSharedCompartment(network1,co1ex,medium);
+            builder.fuseCompartmentIntoSharedCompartment(network2,co2ex,medium);
+        }else{
+            builder.bumpCompartmentIntoSharedCompartment(network1,co1ex,medium);
+            builder.bumpCompartmentIntoSharedCompartment(network2,co2ex,medium);
+        }
+        BioNetwork metaNetwork = builder.build();
+        System.out.println(" Done.");
+
+        //export the meta-network
+        System.out.print("Exporting MetaNetwork...");
+        new JsbmlWriter(outputPath,metaNetwork).write();
+        System.out.println(" Done.");
+
+    }
+
+    @Override
+    public String getLabel() {return this.getClass().getSimpleName();}
+
+    @Override
+    public String getLongDescription() {
+        return "Create a Meta-Network from two sub-networks in SBML format.";
+    }
+
+    @Override
+    public String getShortDescription() {
+        return "Create a Meta-Network from two sub-networks in SBML format. A meta-network is a single model which contains several sub-networks that remains individualized within" +
+                "the meta-network (as opposed to models fusion), but which can share some of their components with " +
+                "other sub-networks through a shared \"medium\" compartment.";
+    }
+}
-- 
GitLab


From 7ba7f81adb038d8b10ce1ecdc271765740fd38a1 Mon Sep 17 00:00:00 2001
From: cfrainay <clement.frainay@inrae.fr>
Date: Fri, 20 Sep 2024 17:20:30 +0200
Subject: [PATCH 04/17] refactoring, isolate builder init

---
 .../reconstruction/CreateMetaNetwork.java     | 45 ++++++++++---------
 1 file changed, 25 insertions(+), 20 deletions(-)

diff --git a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
index d5767c203..fa81d37d7 100644
--- a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
+++ b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
@@ -111,6 +111,29 @@ public class CreateMetaNetwork extends AbstractMet4jApplication {
         BioCompartment medium=new BioCompartment("medium"); medium.setName("medium");
         HashMap<BioNetwork,String> alias = new HashMap<>();
         alias.put(network1,n1prefix);alias.put(network2,n2prefix);
+        CommunityNetworkBuilder builder = initMetaNetworkBuilder(medium, alias);
+        builder.add(network1);
+        builder.add(network2);
+
+        //build meta-network
+        if(!keepCompartment){
+            builder.fuseCompartmentIntoSharedCompartment(network1,co1ex,medium);
+            builder.fuseCompartmentIntoSharedCompartment(network2,co2ex,medium);
+        }else{
+            builder.bumpCompartmentIntoSharedCompartment(network1,co1ex,medium);
+            builder.bumpCompartmentIntoSharedCompartment(network2,co2ex,medium);
+        }
+        BioNetwork metaNetwork = builder.build();
+        System.out.println(" Done.");
+
+        //export the meta-network
+        System.out.print("Exporting MetaNetwork...");
+        new JsbmlWriter(outputPath,metaNetwork).write();
+        System.out.println(" Done.");
+
+    }
+
+    private CommunityNetworkBuilder initMetaNetworkBuilder(BioCompartment medium, HashMap<BioNetwork, String> alias) {
         CommunityNetworkBuilder builder = new CommunityNetworkBuilder(medium);
         builder.setEntityFactory(new PrefixedMetaEntityFactory(alias,"pool"));
         Function<BioMetabolite, String> getSharedIdFunction;
@@ -120,7 +143,7 @@ public class CreateMetaNetwork extends AbstractMet4jApplication {
                     try {
                         BioRef r = x.getRefs("metanetx.chemical").iterator().next();
                         return r.getId();
-                    } catch (NoSuchElementException|java.lang.NullPointerException e) {
+                    } catch (NoSuchElementException | NullPointerException e) {
                         return "unknown_"+UUID.randomUUID();
                     }
                 };
@@ -135,25 +158,7 @@ public class CreateMetaNetwork extends AbstractMet4jApplication {
                 throw new IllegalArgumentException("Invalid merging criterion: " + mergingCriterion);
         }
         builder.setGetSharedIdFunction(getSharedIdFunction);
-        builder.add(network1);
-        builder.add(network2);
-
-        //build meta-network
-        if(!keepCompartment){
-            builder.fuseCompartmentIntoSharedCompartment(network1,co1ex,medium);
-            builder.fuseCompartmentIntoSharedCompartment(network2,co2ex,medium);
-        }else{
-            builder.bumpCompartmentIntoSharedCompartment(network1,co1ex,medium);
-            builder.bumpCompartmentIntoSharedCompartment(network2,co2ex,medium);
-        }
-        BioNetwork metaNetwork = builder.build();
-        System.out.println(" Done.");
-
-        //export the meta-network
-        System.out.print("Exporting MetaNetwork...");
-        new JsbmlWriter(outputPath,metaNetwork).write();
-        System.out.println(" Done.");
-
+        return builder;
     }
 
     @Override
-- 
GitLab


From e565319c889beaebe59aa9df503a9c5e7b238387 Mon Sep 17 00:00:00 2001
From: cfrainay <clement.frainay@inrae.fr>
Date: Tue, 24 Sep 2024 18:10:54 +0200
Subject: [PATCH 05/17] enable iterative metanetwork building

---
 .../reconstruction/CreateMetaNetwork.java       | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
index fa81d37d7..7815cf68f 100644
--- a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
+++ b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
@@ -47,6 +47,9 @@ public class CreateMetaNetwork extends AbstractMet4jApplication {
     @Option(name = "-k", aliases = {"--keepCompartment"}, usage = "keep the original external compartments in the meta-network, otherwise, they will be fused into the new shared external compartment", required = false)
     public boolean keepCompartment = false;
 
+    @Option(name = "-n1meta", aliases = {"--firstAsMeta"}, usage = "Treat first network as meta-network, allowing more than two sub-models with iterative fusions. This will overwrite shared compartment and pool compounds (which must follow the \"pool_\" prefix convention) and will ignore --n1prefix argument", required = false)
+    public boolean firstIsMeta = false;
+
     enum strategy {by_metanetx, by_name, by_id}
     @Option(name = "-mc", aliases = {"--mergingCriterion"}, usage = "field used to identify the same metabolites across the two different networks. " +
             "\"by_name\"/\"by_id\" can be used if names/identifiers are consistent and unambiguous across source models, \"by_metanetx\" can be used if models contains MetaNetX identifiers in annotation field using standard miriam format.")
@@ -86,6 +89,14 @@ public class CreateMetaNetwork extends AbstractMet4jApplication {
             System.err.println("Error: external compartment " + external1 + " not found in network 1");
             System.exit(1);
         }
+        if(firstIsMeta){
+            n1prefix = "";
+            network1.removeOnCascade(network1
+                    .getCompartment(external1)
+                    .getComponentsView().stream()
+                    .filter(e -> e instanceof BioMetabolite)
+                    .filter(m -> m.getId().startsWith("pool_")).toArray(BioEntity[]::new));
+        }
         System.out.println(" Done.");
 
 
@@ -120,7 +131,11 @@ public class CreateMetaNetwork extends AbstractMet4jApplication {
             builder.fuseCompartmentIntoSharedCompartment(network1,co1ex,medium);
             builder.fuseCompartmentIntoSharedCompartment(network2,co2ex,medium);
         }else{
-            builder.bumpCompartmentIntoSharedCompartment(network1,co1ex,medium);
+            if(firstIsMeta) {
+                builder.fuseCompartmentIntoSharedCompartment(network1, co1ex, medium);
+            }else{
+                builder.bumpCompartmentIntoSharedCompartment(network1, co1ex, medium);
+            }
             builder.bumpCompartmentIntoSharedCompartment(network2,co2ex,medium);
         }
         BioNetwork metaNetwork = builder.build();
-- 
GitLab


From c4c0d01452c2891adfc44398de8fee94acc0ccce Mon Sep 17 00:00:00 2001
From: cfrainay <clement.frainay@inrae.fr>
Date: Fri, 4 Oct 2024 14:46:57 +0200
Subject: [PATCH 06/17] create exchange reaction, suffix separator as parameter

---
 .../multinetwork/MetaNetworkBuilder.java       | 18 ++++++++++++++++++
 .../PrefixedMetaEntityFactory.java             | 11 ++++++-----
 .../reconstruction/CreateMetaNetwork.java      | 16 +++++++++++-----
 3 files changed, 35 insertions(+), 10 deletions(-)

diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
index b5b5b2958..7d4da1b58 100644
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
@@ -34,11 +34,21 @@ public class MetaNetworkBuilder implements MultiNetworkBuilder{
     @Setter
     private String sharedTransportPrefix = "transport_";
     @Setter
+    private String exchangeReactionPrefix = "exchange_";
+    @Setter
     protected BiFunction<BioMetabolite, BioMetabolite, BioReaction> createLinkWithSharedPool = (m, pool) -> {
         BioReaction r=new BioReaction(poolReactionPrefix+ m.getId());
         r.setReversible(true);
         return r;
     };
+
+    @Setter
+    protected Function<BioMetabolite, BioReaction> createPoolExchangeReaction = (pool) -> {
+        BioReaction r=new BioReaction(exchangeReactionPrefix+pool.getId());
+        r.setReversible(true);
+        return r;
+    };
+
     @Setter
     protected BiFunction<BioMetabolite, BioCompartment, BioReaction> createTransportWithSharedComp = (m, comp) -> {
         BioReaction r=new BioReaction(sharedTransportPrefix+ m.getId()+"_to_"+comp.getId());
@@ -50,6 +60,8 @@ public class MetaNetworkBuilder implements MultiNetworkBuilder{
     protected MetaEntityFactory entityFactory;
     protected HashMap<BioCompartment,BioCompartment> fuseMap= new HashMap<>();
     protected Boolean keepGPR = false;
+    @Setter
+    public Boolean addExchangeReaction = true;
 
     /**
      * enable a compartment to be shared between multiple organisms
@@ -310,6 +322,12 @@ public class MetaNetworkBuilder implements MultiNetworkBuilder{
                     meta.affectRight(r,1.0, sharedComp,e);
                     meta.affectLeft(r,1.0, sharedComp,pool);
                 }
+                //if option selected, add exchange reaction to the pool for flux modeling
+                if(addExchangeReaction){
+                    BioReaction r = this.createPoolExchangeReaction.apply(pool);
+                    meta.add(r);
+                    meta.affectLeft(r,1.0, sharedComp,pool);
+                }
             }
         }
     }
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java
index 3137021eb..2b26d4964 100644
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java
@@ -18,6 +18,7 @@ public class PrefixedMetaEntityFactory implements MetaEntityFactory {
 
     Map<BioNetwork,String> sourcePrefixMap;
     String poolPrefix;
+    String sep = "";
     
     public PrefixedMetaEntityFactory(Map<BioNetwork,String> sourcePrefixMap, String poolPrefix){
         this.sourcePrefixMap=sourcePrefixMap;
@@ -27,13 +28,13 @@ public class PrefixedMetaEntityFactory implements MetaEntityFactory {
     /**
      * create prefix-id from network alias
      */
-    public BiFunction<String, BioNetwork, String> addSourcePrefix = (id, bn) -> sourcePrefixMap.get(bn)+"_"+id;
+    public BiFunction<String, BioNetwork, String> addSourcePrefix = (id, bn) -> sourcePrefixMap.get(bn)+sep+id;
 
     /**
      * create sharedCompartment-suffix id
      */
-    public BiFunction<String, BioCompartment, String> addCompSuffix = (id, comp) -> id+"_"+comp.getId();
-    public BiFunction<String, BioCompartment, String> addCompPreffix = (id, comp) -> comp.getId()+"_"+id;
+    public BiFunction<String, BioCompartment, String> addCompSuffix = (id, comp) -> id+sep+comp.getId();
+    public BiFunction<String, BioCompartment, String> addCompPreffix = (id, comp) -> comp.getId()+sep+id;
 
 
     /**
@@ -41,7 +42,7 @@ public class PrefixedMetaEntityFactory implements MetaEntityFactory {
      */
     public Function<String, String> removeSourceSuffix = id -> {
             for(String s : sourcePrefixMap.values()) {
-                id = id.replaceAll("^" + s + "_","");
+                id = id.replaceAll("^" + s + sep,"");
             };
             return id;
         };
@@ -49,7 +50,7 @@ public class PrefixedMetaEntityFactory implements MetaEntityFactory {
     /**
      * create pool-prefix id
      */
-    public Function<String, String> addPoolFlag = (id) -> poolPrefix+"_"+id;
+    public Function<String, String> addPoolFlag = (id) -> poolPrefix+sep+id;
 
 
     @SneakyThrows
diff --git a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
index 7815cf68f..50204fe75 100644
--- a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
+++ b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
@@ -22,7 +22,7 @@ import java.util.function.Function;
 
 import static fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.EnumFormats.Sbml;
 import static fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.EnumParameterTypes.InputFile;
-import static fr.inrae.toulouse.metexplore.met4j_toolbox.reconstruction.CreateMetaNetwork.strategy.*;
+import static fr.inrae.toulouse.metexplore.met4j_toolbox.reconstruction.CreateMetaNetwork.strategy.by_name;
 
 public class CreateMetaNetwork extends AbstractMet4jApplication {
 
@@ -91,11 +91,15 @@ public class CreateMetaNetwork extends AbstractMet4jApplication {
         }
         if(firstIsMeta){
             n1prefix = "";
-            network1.removeOnCascade(network1
-                    .getCompartment(external1)
+            BioMetabolite[] pool1 = network1.getCompartment(external1)
                     .getComponentsView().stream()
                     .filter(e -> e instanceof BioMetabolite)
-                    .filter(m -> m.getId().startsWith("pool_")).toArray(BioEntity[]::new));
+                    .filter(m -> m.getId().startsWith("pool_"))
+                    .toArray(BioMetabolite[]::new);
+            for(BioMetabolite pool : pool1){
+                network1.removeOnCascade(network1.getReactionsFromMetabolite(pool));
+            }
+            network1.removeOnCascade(pool1);
         }
         System.out.println(" Done.");
 
@@ -150,7 +154,9 @@ public class CreateMetaNetwork extends AbstractMet4jApplication {
 
     private CommunityNetworkBuilder initMetaNetworkBuilder(BioCompartment medium, HashMap<BioNetwork, String> alias) {
         CommunityNetworkBuilder builder = new CommunityNetworkBuilder(medium);
-        builder.setEntityFactory(new PrefixedMetaEntityFactory(alias,"pool"));
+        PrefixedMetaEntityFactory factory = new PrefixedMetaEntityFactory(alias,"pool_");
+        factory.addCompSuffix = (id, comp) -> id+"_"+comp.getId();
+        builder.setEntityFactory(factory);
         Function<BioMetabolite, String> getSharedIdFunction;
         switch (mergingCriterion) {
             case by_metanetx:
-- 
GitLab


From 762840f67bf6451a953915f6aadb5b881f509175 Mon Sep 17 00:00:00 2001
From: cfrainay <clement.frainay@inrae.fr>
Date: Tue, 8 Oct 2024 17:34:38 +0200
Subject: [PATCH 07/17] add test + better handling of prefixed pool compounds

---
 .../multinetwork/MetaNetworkBuilder.java      |   2 +-
 .../PrefixedMetaEntityFactory.java            |  11 +-
 .../multinetwork/TestMetaNetworkBuilder.java  | 108 +++++++++++++++++-
 3 files changed, 114 insertions(+), 7 deletions(-)

diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
index 7d4da1b58..9c214f4b0 100644
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
@@ -61,7 +61,7 @@ public class MetaNetworkBuilder implements MultiNetworkBuilder{
     protected HashMap<BioCompartment,BioCompartment> fuseMap= new HashMap<>();
     protected Boolean keepGPR = false;
     @Setter
-    public Boolean addExchangeReaction = true;
+    protected Boolean addExchangeReaction = true;
 
     /**
      * enable a compartment to be shared between multiple organisms
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java
index 2b26d4964..7cb874ebb 100644
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java
@@ -4,6 +4,7 @@ import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioCompartment;
 import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioEntity;
 import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioMetabolite;
 import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork;
+import lombok.Setter;
 import lombok.SneakyThrows;
 
 import java.lang.reflect.InvocationTargetException;
@@ -14,11 +15,15 @@ import java.util.Map;
 import java.util.function.BiFunction;
 import java.util.function.Function;
 
+import static fr.inrae.toulouse.metexplore.met4j_core.utils.StringUtils.isVoid;
+
 public class PrefixedMetaEntityFactory implements MetaEntityFactory {
 
     Map<BioNetwork,String> sourcePrefixMap;
-    String poolPrefix;
-    String sep = "";
+    @Setter
+    protected String poolPrefix;
+    @Setter
+    protected String sep = "_";
     
     public PrefixedMetaEntityFactory(Map<BioNetwork,String> sourcePrefixMap, String poolPrefix){
         this.sourcePrefixMap=sourcePrefixMap;
@@ -28,7 +33,7 @@ public class PrefixedMetaEntityFactory implements MetaEntityFactory {
     /**
      * create prefix-id from network alias
      */
-    public BiFunction<String, BioNetwork, String> addSourcePrefix = (id, bn) -> sourcePrefixMap.get(bn)+sep+id;
+    public BiFunction<String, BioNetwork, String> addSourcePrefix = (id, bn) -> isVoid(sourcePrefixMap.get(bn)) ? id : sourcePrefixMap.get(bn)+sep+id;
 
     /**
      * create sharedCompartment-suffix id
diff --git a/met4j-core/src/test/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/TestMetaNetworkBuilder.java b/met4j-core/src/test/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/TestMetaNetworkBuilder.java
index 75a02f9fb..31d2bbd88 100644
--- a/met4j-core/src/test/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/TestMetaNetworkBuilder.java
+++ b/met4j-core/src/test/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/TestMetaNetworkBuilder.java
@@ -1,10 +1,16 @@
 package fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork;
 
-import fr.inrae.toulouse.metexplore.met4j_core.biodata.*;
+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.Arrays;
 import java.util.HashMap;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import static org.junit.Assert.*;
 
@@ -27,7 +33,10 @@ public class TestMetaNetworkBuilder {
         medium2=new BioCompartment("medium2");
         CommunityNetworkBuilder builder = new CommunityNetworkBuilder(medium);
         builder.addNewSharedCompartment(medium2);
-        builder.setEntityFactory(new PrefixedMetaEntityFactory(alias,"pool"));
+        PrefixedMetaEntityFactory factory = new PrefixedMetaEntityFactory(alias,"pool");
+        factory.setSep("_");
+        builder.setEntityFactory(factory);
+        builder.setAddExchangeReaction(false);
         builder.add(bn1);
         builder.add(bn2);
         builder.add(bn3);
@@ -50,7 +59,10 @@ public class TestMetaNetworkBuilder {
         medium2=new BioCompartment("medium2");
         CommunityNetworkBuilder builder = new CommunityNetworkBuilder(medium);
         builder.addNewSharedCompartment(medium2);
-        builder.setEntityFactory(new PrefixedMetaEntityFactory(alias,"pool"));
+        PrefixedMetaEntityFactory factory = new PrefixedMetaEntityFactory(alias,"pool");
+        factory.setSep("_");
+        builder.setEntityFactory(factory);
+        builder.setAddExchangeReaction(false);
         builder.add(bn1,co1ex);
         builder.add(bn2,co2ex);
         builder.add(bn3,co3ex);
@@ -59,6 +71,65 @@ public class TestMetaNetworkBuilder {
         return builder.build();
     }
 
+    public static BioNetwork initFuseIncremental() {
+        HashMap<BioNetwork,String> alias = new HashMap<>();
+        alias.put(bn1,"model1");
+        alias.put(bn2,"model2");
+
+        BioCompartment medium0=new BioCompartment("medium");
+        CommunityNetworkBuilder builder = new CommunityNetworkBuilder(medium0);
+        PrefixedMetaEntityFactory factory = new PrefixedMetaEntityFactory(alias,"pool");
+        factory.setSep("_");
+        builder.setEntityFactory(factory);
+        builder.setAddExchangeReaction(false);
+        builder.add(bn1,co1ex);
+        builder.add(bn2,co2ex);
+        BioNetwork firstIter = builder.build();
+
+        System.out.println(Arrays.toString(bn1.getMetabolitesView().stream().map(x -> x.getId()).toArray()));
+        System.out.println(Arrays.toString(bn1.getReactionsView().stream().map(x -> x.getId()).toArray()));
+        System.out.println("+");
+        System.out.println(Arrays.toString(bn2.getMetabolitesView().stream().map(x -> x.getId()).toArray()));
+        System.out.println(Arrays.toString(bn2.getReactionsView().stream().map(x -> x.getId()).toArray()));
+        System.out.println("=");
+        System.out.println(Arrays.toString(firstIter.getMetabolitesView().stream().map(x -> x.getId()).toArray()));
+        System.out.println(Arrays.toString(firstIter.getReactionsView().stream().map(x -> x.getId()).toArray()));
+
+        BioMetabolite[] pool1 = firstIter.getCompartment("medium")
+                .getComponentsView().stream()
+                .filter(e -> e instanceof BioMetabolite)
+                .filter(m -> m.getId().startsWith("pool_"))
+                .toArray(BioMetabolite[]::new);
+        for(BioMetabolite pool : pool1){
+            firstIter.removeOnCascade(firstIter.getReactionsFromMetabolite(pool));
+        }
+        firstIter.removeOnCascade(pool1);
+
+        System.out.println("->");
+        System.out.println(Arrays.toString(firstIter.getMetabolitesView().stream().map(x -> x.getId()).toArray()));
+        System.out.println(Arrays.toString(firstIter.getReactionsView().stream().map(x -> x.getId()).toArray()));
+
+        medium=new BioCompartment("medium");
+        medium2=new BioCompartment("medium2");
+        builder = new CommunityNetworkBuilder(medium);
+        builder.addNewSharedCompartment(medium2);
+        alias = new HashMap<>();
+        alias.put(firstIter,"");
+        alias.put(bn3,"model3");
+        alias.put(bn4,"model4");
+        builder.setEntityFactory(new PrefixedMetaEntityFactory(alias,"pool"));
+        builder.setAddExchangeReaction(false);
+        builder.add(firstIter,medium0);
+        builder.add(bn3,co3ex);
+        builder.add(bn4);
+        builder.fuseCompartmentIntoSharedCompartment(bn4,co4ex,medium2);
+
+        BioNetwork output = builder.build();
+        System.out.println(Arrays.toString(output.getMetabolitesView().stream().map(x -> x.getId()).toArray()));
+        System.out.println(Arrays.toString(output.getReactionsView().stream().map(x -> x.getId()).toArray()));
+        return output;
+    }
+
     @BeforeClass
     public static void beforeClass() {
         bn1 = new BioNetwork("bn1");
@@ -147,6 +218,37 @@ public class TestMetaNetworkBuilder {
         assertEquals(6,meta.getCompartmentsView().size());
     }
 
+    @Test
+    public void testCompoundsFuseIncremental(){
+        BioNetwork meta = initFuseIncremental();
+        assertEquals(14,meta.getMetabolitesView().size());
+        assertNotNull(meta.getMetabolite("model1_a_in"));
+        assertNotNull(meta.getMetabolite("model1_c_in"));
+        assertNotNull(meta.getMetabolite("model1_c_ex"));
+        assertNotNull(meta.getMetabolite("model2_c_ex"));
+        assertNotNull(meta.getMetabolite("model2_c_in"));
+
+        Set<String> metabolites = meta.getMetabolitesView().stream().map(x -> x.getId()).collect(Collectors.toSet());
+        assertTrue(metabolites.contains("pool_medium_model1_c_ex")||metabolites.contains("pool_medium_model2_c_ex"));
+        assertNotNull(meta.getMetabolite("pool_medium_d_ex"));
+        assertNotNull(meta.getMetabolite("pool_medium2_d_ex"));
+    }
+
+    @Test
+    public void testReactionsFuseIncremental() {
+        BioNetwork meta = initFuseIncremental();
+        assertEquals(10, meta.getReactionsView().size());
+        assertNotNull(meta.getReaction("poolReaction_model2_c_ex"));
+        assertNotNull(meta.getReaction("poolReaction_model1_c_ex"));
+        assertNotNull(meta.getReaction("poolReaction_model3_d_ex"));
+    }
+
+    @Test
+    public void testCompartmentFuseIncremental(){
+        BioNetwork meta = initFuseIncremental();
+        assertEquals(6,meta.getCompartmentsView().size());
+    }
+
 
     @Test
     public void testCompoundsBump(){
-- 
GitLab


From c6c40d9f56b3bd464de80c207b405ad86d89e3c6 Mon Sep 17 00:00:00 2001
From: cfrainay <clement.frainay@inrae.fr>
Date: Thu, 10 Oct 2024 15:57:55 +0200
Subject: [PATCH 08/17] fix sbml annotation handling and export issue

---
 .../reconstruction/CreateMetaNetwork.java        | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
index 50204fe75..925b914a0 100644
--- a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
+++ b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
@@ -3,8 +3,11 @@ package fr.inrae.toulouse.metexplore.met4j_toolbox.reconstruction;
 import fr.inrae.toulouse.metexplore.met4j_core.biodata.*;
 import fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork.CommunityNetworkBuilder;
 import fr.inrae.toulouse.metexplore.met4j_core.biodata.multinetwork.PrefixedMetaEntityFactory;
+import fr.inrae.toulouse.metexplore.met4j_io.annotations.network.NetworkAttributes;
 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_io.jsbml.reader.plugin.*;
+import fr.inrae.toulouse.metexplore.met4j_io.jsbml.units.BioUnitDefinition;
 import fr.inrae.toulouse.metexplore.met4j_io.jsbml.writer.JsbmlWriter;
 import fr.inrae.toulouse.metexplore.met4j_io.jsbml.writer.Met4jSbmlWriterException;
 import fr.inrae.toulouse.metexplore.met4j_toolbox.generic.AbstractMet4jApplication;
@@ -14,10 +17,7 @@ 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.util.HashMap;
-import java.util.NoSuchElementException;
-import java.util.Objects;
-import java.util.UUID;
+import java.util.*;
 import java.util.function.Function;
 
 import static fr.inrae.toulouse.metexplore.met4j_toolbox.generic.annotations.EnumFormats.Sbml;
@@ -76,9 +76,12 @@ public class CreateMetaNetwork extends AbstractMet4jApplication {
         //import networks
         System.out.print("Importing network 1...");
         JsbmlReader reader = new JsbmlReader(this.sbml1FilePath);
+        ArrayList<PackageParser> pkgs = new ArrayList<>(Arrays.asList(
+                new NotesParser(false), new AnnotationParser(true), new FBCParser(), new GroupPathwayParser()));
+
         BioNetwork network1 = null;
         try {
-            network1 = reader.read();
+            network1 = reader.read(pkgs);
         } catch (Met4jSbmlReaderException e) {
             System.err.println("Error while reading the first SBML file");
             System.err.println(e.getMessage());
@@ -108,7 +111,7 @@ public class CreateMetaNetwork extends AbstractMet4jApplication {
         reader = new JsbmlReader(this.sbml2FilePath);
         BioNetwork network2 = null;
         try {
-            network2 = reader.read();
+            network2 = reader.read(pkgs);
         } catch (Met4jSbmlReaderException e) {
             System.err.println("Error while reading the second SBML file");
             System.err.println(e.getMessage());
@@ -147,6 +150,7 @@ public class CreateMetaNetwork extends AbstractMet4jApplication {
 
         //export the meta-network
         System.out.print("Exporting MetaNetwork...");
+        NetworkAttributes.addUnitDefinition(metaNetwork, new BioUnitDefinition());
         new JsbmlWriter(outputPath,metaNetwork).write();
         System.out.println(" Done.");
 
-- 
GitLab


From 280df1461eb5173e0d7cbbc97a82692ff2e6fdef Mon Sep 17 00:00:00 2001
From: Clement Frainay <clement.frainay@inra.fr>
Date: Tue, 29 Oct 2024 17:00:05 +0100
Subject: [PATCH 09/17] fix typo

---
 .../met4j_core/biodata/multinetwork/MetaNetworkBuilder.java     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
index 9c214f4b0..da8edbbc2 100644
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
@@ -11,7 +11,7 @@ import java.util.function.Function;
 import java.util.stream.Collectors;
 
 /**
- * A class that creates a meta-networks from multiple sub-networks. A meta-network is a single model which contains several
+ * A class that creates a meta-network from multiple sub-networks. A meta-network is a single model which contains several
  * sub-networks that remains individualized within the meta-network (as opposed to models fusion), but which can share
  * some of their components with other sub-networks through shared compartments.
  */
-- 
GitLab


From 79a9ec4f7dd974c50420c87e7736c5653c4703a2 Mon Sep 17 00:00:00 2001
From: Clement Frainay <clement.frainay@inra.fr>
Date: Tue, 29 Oct 2024 17:00:21 +0100
Subject: [PATCH 10/17] fix typo

---
 .../met4j_core/biodata/multinetwork/MetaNetworkBuilder.java     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
index da8edbbc2..571630093 100644
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
@@ -12,7 +12,7 @@ import java.util.stream.Collectors;
 
 /**
  * A class that creates a meta-network from multiple sub-networks. A meta-network is a single model which contains several
- * sub-networks that remains individualized within the meta-network (as opposed to models fusion), but which can share
+ * sub-networks that remain individualized within the meta-network (as opposed to models fusion), but which can share
  * some of their components with other sub-networks through shared compartments.
  */
 public class MetaNetworkBuilder implements MultiNetworkBuilder{
-- 
GitLab


From 1efe74591ae056f88aa740a0c4e7ff8e23140b38 Mon Sep 17 00:00:00 2001
From: Clement Frainay <clement.frainay@inra.fr>
Date: Tue, 29 Oct 2024 17:00:31 +0100
Subject: [PATCH 11/17] fix typo

---
 .../met4j_core/biodata/multinetwork/MetaNetworkBuilder.java     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
index 571630093..9ddd7f131 100644
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
@@ -73,7 +73,7 @@ public class MetaNetworkBuilder implements MultiNetworkBuilder{
 
     /**
      * Fuse a sub-network's compartment into a meta-network's shared compartment. The former will be replaced by the latter during build.
-     * All compartment's component will be added to the shared compartment
+     * All compartment's components will be added to the shared compartment
      * @param n
      * @param c
      * @param sc
-- 
GitLab


From c9312b649fb0f17f44d3d5884f41c120e6584d56 Mon Sep 17 00:00:00 2001
From: Clement Frainay <clement.frainay@inra.fr>
Date: Tue, 29 Oct 2024 17:02:09 +0100
Subject: [PATCH 12/17] fix description method

---
 .../met4j_toolbox/reconstruction/CreateMetaNetwork.java         | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
index 925b914a0..d41bf6285 100644
--- a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
+++ b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
@@ -190,7 +190,7 @@ public class CreateMetaNetwork extends AbstractMet4jApplication {
     public String getLabel() {return this.getClass().getSimpleName();}
 
     @Override
-    public String getLongDescription() {
+    public String getShortDescription() {
         return "Create a Meta-Network from two sub-networks in SBML format.";
     }
 
-- 
GitLab


From 87303bd748126410d6491e4a810e6ee48ca80aee Mon Sep 17 00:00:00 2001
From: cfrainay <clement.frainay@inrae.fr>
Date: Tue, 29 Oct 2024 17:02:59 +0100
Subject: [PATCH 13/17] fix description method

---
 .../met4j_toolbox/reconstruction/CreateMetaNetwork.java         | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
index d41bf6285..574000ec2 100644
--- a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
+++ b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
@@ -195,7 +195,7 @@ public class CreateMetaNetwork extends AbstractMet4jApplication {
     }
 
     @Override
-    public String getShortDescription() {
+    public String getLongDescription() {
         return "Create a Meta-Network from two sub-networks in SBML format. A meta-network is a single model which contains several sub-networks that remains individualized within" +
                 "the meta-network (as opposed to models fusion), but which can share some of their components with " +
                 "other sub-networks through a shared \"medium\" compartment.";
-- 
GitLab


From aecf043504a613000b649d41262a309419e9f9c2 Mon Sep 17 00:00:00 2001
From: cfrainay <clement.frainay@inrae.fr>
Date: Tue, 29 Oct 2024 17:17:36 +0100
Subject: [PATCH 14/17] fix javadoc

---
 .../multinetwork/MetaNetworkBuilder.java      | 28 ++++++++++++-------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
index 9ddd7f131..36cd946d2 100644
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
@@ -65,7 +65,7 @@ public class MetaNetworkBuilder implements MultiNetworkBuilder{
 
     /**
      * enable a compartment to be shared between multiple organisms
-     * @param sc
+     * @param sc a compartment that will be shared between multiple bionetworks
      */
     public void addNewSharedCompartment(BioCompartment sc) {
         metaCompComposition.put(sc,new HashSet<>());
@@ -74,9 +74,9 @@ public class MetaNetworkBuilder implements MultiNetworkBuilder{
     /**
      * Fuse a sub-network's compartment into a meta-network's shared compartment. The former will be replaced by the latter during build.
      * All compartment's components will be added to the shared compartment
-     * @param n
-     * @param c
-     * @param sc
+     * @param n the sub-network
+     * @param c the sub-network's compartment to be fused
+     * @param sc the shared meta-compartment that will receive the compartment's components
      */
     public void fuseCompartmentIntoSharedCompartment(BioNetwork n, BioCompartment c, BioCompartment sc) {
         if(!metaCompComposition.containsKey(sc)) throw new IllegalArgumentException("Shared meta-compartment "+sc.getId()+" not found in network");
@@ -86,9 +86,9 @@ public class MetaNetworkBuilder implements MultiNetworkBuilder{
     /**
      * Bump a sub-network's compartment into a meta-network's shared compartment. For each compartment's component,
      * a copy is created within the shared compartment
-     * @param n
-     * @param c
-     * @param sc
+     * @param n the sub-network
+     * @param c the sub-network's compartment to be bumped
+     * @param sc the shared meta-compartment that will receive the compartment's components
      */
     public void bumpCompartmentIntoSharedCompartment(BioNetwork n, BioCompartment c, BioCompartment sc) {
         BioCollection<BioMetabolite> toShare = n.getMetabolitesView().stream()
@@ -98,7 +98,7 @@ public class MetaNetworkBuilder implements MultiNetworkBuilder{
     }
 
     /**
-     *
+     * Exchange a compound from a sub-network's compartment to a meta-network's shared compartment. A copy of the compound will be created
      * @param sourceNetwork the original subnetwork of the compound
      * @param sourceCompartment  the original compartment of the compound in the subnetwork
      * @param metabolite the compound
@@ -110,7 +110,7 @@ public class MetaNetworkBuilder implements MultiNetworkBuilder{
     }
 
     /**
-     *
+     * Exchange compounds from a sub-network's compartment to a meta-network's shared compartment. A copy of each exchanged compound will be created
      * @param sourceNetwork the original subnetwork of the compounds
      * @param sourceCompartment  the original compartment of the compounds in the subnetwork
      * @param metabolites the compounds
@@ -254,6 +254,14 @@ public class MetaNetworkBuilder implements MultiNetworkBuilder{
         }
     }
 
+    /**
+     * Set the alias prefixes for the entities of the sub-networks. This allows to distinguish entities from different sub-networks
+     * that have the same name. The prefixes are added to the entity names in the meta-network.
+     * @param aliases a map of sub-networks and their respective prefixes
+     *                (e.g. {subNetwork1: "prefix1", subNetwork2: "prefix2"})
+     *                The prefixes are added to the entity names in the meta-network.
+     *                If a sub-network is not in the map, or is in the map but has a null or empty prefix, its entities will not be prefixed.
+     **/
     public void setAliasPrefixes(Map<BioNetwork,String> aliases, String poolPrefix){
         this.setEntityFactory(new PrefixedMetaEntityFactory(aliases, poolPrefix));
     }
@@ -296,7 +304,7 @@ public class MetaNetworkBuilder implements MultiNetworkBuilder{
     /**
      * Links the source-specific metabolites representing the same compounds in the shared compartments, by creating a shared "pool" entity
      * alongside their interconversion reactions.
-     * @param meta
+     * @param meta the meta-network
      */
     protected void linkCompoundsInSharedComp(BioNetwork meta) {
         //loop over each shared compartment
-- 
GitLab


From e0af67341446ae825c49b94158ab785b3a7ec2b9 Mon Sep 17 00:00:00 2001
From: cfrainay <clement.frainay@inrae.fr>
Date: Fri, 8 Nov 2024 17:17:57 +0100
Subject: [PATCH 15/17] add doc

---
 .../multinetwork/MetaEntityFactory.java       | 24 +++++++++++++++++++
 .../PrefixedMetaEntityFactory.java            | 16 +++++++++----
 2 files changed, 36 insertions(+), 4 deletions(-)

diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaEntityFactory.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaEntityFactory.java
index c7254a7cf..9b709b4ab 100644
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaEntityFactory.java
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaEntityFactory.java
@@ -7,10 +7,34 @@ import fr.inrae.toulouse.metexplore.met4j_core.biodata.BioNetwork;
 
 import java.util.Collection;
 
+/**
+* Factory to create new instances of BioEntity and avoid id conflicts between subnetworks in a meta-graph.
+**/
 public interface MetaEntityFactory {
 
+    /**
+    * Create a copy of a subnetwork's BioEntity for its MetaGraph, while guaranteeing no id conflicts if the same
+    * original id was used in another subnetworks. This may be done by referencing in the new id the source network
+    * passed as parameter.
+    * @param originalEntity the original entity
+    * @param source the source network
+    **/
     <E extends BioEntity> E createMetaEntity(E originalEntity, BioNetwork source);
+
+    /**
+     * Create a copy of a subnetwork's BioMetabolite in a shared compartment.
+     * @param originalEntity the original entity
+     * @param sharedComp the target shared compartment
+     * @return
+     */
     BioMetabolite createSharedCompound(BioMetabolite originalEntity, BioCompartment sharedComp);
+
+    /**
+     * Create a new BioMetabolite in a shared compartment, that represent a pool of compounds from different subnetworks.
+     * @param entities the matching entities in different subnetworks
+     * @param sharedComp the target shared compartment
+     * @return
+     */
     BioMetabolite createPoolCompound (Collection<BioMetabolite> entities, BioCompartment sharedComp);
 
 }
diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java
index 7cb874ebb..29437aa06 100644
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/PrefixedMetaEntityFactory.java
@@ -17,6 +17,10 @@ import java.util.function.Function;
 
 import static fr.inrae.toulouse.metexplore.met4j_core.utils.StringUtils.isVoid;
 
+/**
+ * Factory to create new instances of BioEntity using prefixed ids from user-defined alias, in order to avoid id conflicts between subnetworks
+ * in a meta-graph.
+ **/
 public class PrefixedMetaEntityFactory implements MetaEntityFactory {
 
     Map<BioNetwork,String> sourcePrefixMap;
@@ -31,7 +35,8 @@ public class PrefixedMetaEntityFactory implements MetaEntityFactory {
     }
 
     /**
-     * create prefix-id from network alias
+     * create prefixed id. This function will be called to create a new id for a BioEntity, using the
+     * alias of the network it comes from (if it exists) and its original id.
      */
     public BiFunction<String, BioNetwork, String> addSourcePrefix = (id, bn) -> isVoid(sourcePrefixMap.get(bn)) ? id : sourcePrefixMap.get(bn)+sep+id;
 
@@ -39,7 +44,10 @@ public class PrefixedMetaEntityFactory implements MetaEntityFactory {
      * create sharedCompartment-suffix id
      */
     public BiFunction<String, BioCompartment, String> addCompSuffix = (id, comp) -> id+sep+comp.getId();
-    public BiFunction<String, BioCompartment, String> addCompPreffix = (id, comp) -> comp.getId()+sep+id;
+    /**
+     * create sharedCompartment-prefix id
+     */
+    public BiFunction<String, BioCompartment, String> addCompPrefix = (id, comp) -> comp.getId()+sep+id;
 
 
     /**
@@ -53,7 +61,7 @@ public class PrefixedMetaEntityFactory implements MetaEntityFactory {
         };
 
     /**
-     * create pool-prefix id
+     * create a pool-prefix id. This is used to identify a pool of compounds originating from different subnetworks.
      */
     public Function<String, String> addPoolFlag = (id) -> poolPrefix+sep+id;
 
@@ -75,7 +83,7 @@ public class PrefixedMetaEntityFactory implements MetaEntityFactory {
     public BioMetabolite createPoolCompound(Collection<BioMetabolite> entities, BioCompartment sharedCompartment) {
         BioMetabolite entity = entities.iterator().next();
         return newEntityInstance(entity, removeSourceSuffix
-                .andThen(s -> addCompPreffix.apply(s,sharedCompartment))
+                .andThen(s -> addCompPrefix.apply(s,sharedCompartment))
                 .andThen(addPoolFlag)
                 .apply(entity.getId()));
     }
-- 
GitLab


From 503bea1a2861ab083dc8dad64cf4949398b22f31 Mon Sep 17 00:00:00 2001
From: Clement Frainay <clement.frainay@inra.fr>
Date: Tue, 3 Dec 2024 14:08:54 +0100
Subject: [PATCH 16/17] add javadoc

---
 .../biodata/multinetwork/MetaNetworkBuilder.java         | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
index 36cd946d2..6180d7b80 100644
--- a/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
+++ b/met4j-core/src/main/java/fr/inrae/toulouse/metexplore/met4j_core/biodata/multinetwork/MetaNetworkBuilder.java
@@ -129,6 +129,11 @@ public class MetaNetworkBuilder implements MultiNetworkBuilder{
         networks.add(bn);
     }
 
+    /**
+     * creates all maps for conversion (original bioentity, new meta-entity)
+     * instantiate meta-network as new bionetwork
+     * @return an empty meta-network
+     */
     protected BioNetwork initMetaNetwork(){
         this.metaboliteConversion = new HashMap<>();
         this.compartmentConversion = new HashMap<>();
@@ -139,6 +144,10 @@ public class MetaNetworkBuilder implements MultiNetworkBuilder{
         return new BioNetwork();
     }
 
+    /**
+     * add all copied bioentity to the newly built meta-network
+     * @param meta the (usually empty) meta-network
+     */
     protected void populateMetaNetwork(BioNetwork meta){
 
         for(BioNetwork sub : networks){
-- 
GitLab


From 6433a6d76363c9e5489c43365630511155f2e831 Mon Sep 17 00:00:00 2001
From: lcottret <ludovic.cottret@inrae.fr>
Date: Fri, 27 Dec 2024 16:36:01 +0100
Subject: [PATCH 17/17] add missing decorator InputFile

---
 .../toulouse/metexplore/met4j_toolbox/GenerateGalaxyFiles.java   | 1 -
 .../met4j_toolbox/reconstruction/CreateMetaNetwork.java          | 1 +
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/GenerateGalaxyFiles.java b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/GenerateGalaxyFiles.java
index ecd9317c2..1d26274c5 100644
--- a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/GenerateGalaxyFiles.java
+++ b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/GenerateGalaxyFiles.java
@@ -57,7 +57,6 @@ import java.lang.reflect.Method;
 import java.net.URL;
 import java.util.Comparator;
 import java.util.List;
-import java.util.Set;
 import java.util.stream.Collectors;
 
 public class GenerateGalaxyFiles extends AbstractMet4jApplication {
diff --git a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
index 574000ec2..a0ef3029a 100644
--- a/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
+++ b/met4j-toolbox/src/main/java/fr/inrae/toulouse/metexplore/met4j_toolbox/reconstruction/CreateMetaNetwork.java
@@ -31,6 +31,7 @@ public class CreateMetaNetwork extends AbstractMet4jApplication {
     @ParameterType(name = InputFile)
     @Option(name = "-n1", aliases = {"--network1"},  usage = "input SBML file: path to first network, in sbml format.", required = true)
     public String sbml1FilePath;
+    @ParameterType(name = InputFile)
     @Option(name = "-n2", aliases = {"--network2"},  usage = "input SBML file: path to second network, in sbml format.", required = true)
     public String sbml2FilePath;
 
-- 
GitLab