Relative ratio size calculations can lead to invalid math
Compartment volume and surface calculations for legacy models that use relative sizes instead of absolute sizes were changed to fix bugs related to overriding size ratio parameters (see #357 and related issues and PRs).

This leads to occasional circular references in generated math which is invalid (and as a result all simulations as well as all exports fail).

Example: biomodel_17028306
Function  Compartment::Size_Cell	 ( - Size_Cell * VolFract_PM / ( - 1.0 + VolFract_PM));
diff --git a/.github/workflows/create_bs_singularity.yml b/.github/workflows/create_bs_singularity.yml
index fd0649cb8..a86b210dc 100644
--- a/.github/workflows/create_bs_singularity.yml
+++ b/.github/workflows/create_bs_singularity.yml
@@ -49,14 +49,14 @@ jobs:
         run: |
           mvn -version
           java -version
-          mvn --batch-mode clean install dependency:copy-dependencies
+          mvn --batch-mode clean install dependency:copy-dependencies -DskipTests=true
       - name: set global environment variables
         run: |
           echo "VCELL_SHA=`git rev-parse --short HEAD`" >> $GITHUB_ENV
       - name: Install Singularity # to make singularity image to use on cluster
         uses: eWaterCycle/setup-singularity@v6
         with:
-          singularity-version: 3.9.2
+          singularity-version: 3.8.3
       - name: Build Docker image and push with latest tag
         run: |
           set -ux
@@ -68,28 +68,16 @@ jobs:
             --no-cache \
             --file Dockerfile \
             --build-arg SIMULATOR_VERSION=${VCELL_SHA} \
-            --tag ghcr.io/virtualcell/biosimulators_vcell:latest \
+            --tag ghcr.io/virtualcell/biosimulators_vcell:${VCELL_SHA} \
             --label org.opencontainers.image.revision=${REVISION} \
             --label org.opencontainers.image.created=${CREATED} \
             --label org.opencontainers.image.version=${VCELL_SHA} \
             --label org.opencontainers.image.license=MIT \
             .
-          docker tag ghcr.io/virtualcell/biosimulators_vcell:${VCELL_SHA}
           docker push --all-tags ghcr.io/virtualcell/biosimulators_vcell
           singularity build biosimulators_vcell_${VCELL_SHA}.img docker:ghcr.io/virtualcell/biosimulators_vcell:${VCELL_SHA}
           singularity remote login -u ${{ secrets.ACTION_USER }} -p ${{ secrets.ACTION_TOKEN }} oras://ghcr.io
           singularity push -U biosimulators_vcell_${VCELL_SHA}.img oras://ghcr.io/virtualcell/biosimulators_vcell_singularity:${VCELL_SHA}
-      - name: Get release upload URL
-        id: get_url
-        uses: pdamianik/release-tag-to-upload-url-action@v1.0.1
-        with:
-          tag: ${VCELL_SHA}
-          token: ${{ github.token }}
-      - name: Upload edited specifications file
-        uses: shogo82148/actions-upload-release-asset@v1
-        with:
-          upload_url: ${{ steps.get_url.outputs.uploadUrl }}
-          asset_path: biosimulators.json
 
       - name: Setup tmate session
         uses: mxschmitt/action-tmate@v3
diff --git a/vcell-cli/src/main/java/org/vcell/cli/vcml/VcmlOmexConverter.java b/vcell-cli/src/main/java/org/vcell/cli/vcml/VcmlOmexConverter.java
index 970e05da6..f08f48d64 100644
--- a/vcell-cli/src/main/java/org/vcell/cli/vcml/VcmlOmexConverter.java
+++ b/vcell-cli/src/main/java/org/vcell/cli/vcml/VcmlOmexConverter.java
@@ -286,9 +286,11 @@ public class VcmlOmexConverter {
         BioModel bioModel = null;
 		try {
 			bioModel = XmlHelper.XMLToBioModel(new XMLSource(vcmlFilePath));       
-			bioModel.updateAll(false);
+//			bioModel.updateAll(false);
+			bioModel.refreshDependencies();
 			writeFileEntry(outputBaseDir, vcmlName + ",VCML,SUCCEEDED\n", jobLogFile, bForceLogFiles);
-		} catch (XmlParseException | MappingException e1) {
+//		} catch (XmlParseException | MappingException e1) {
+		} catch (XmlParseException e1) {
 			// TODO Auto-generated catch block
 			e1.printStackTrace();
 			writeFileEntry(outputBaseDir, vcmlName + ",VCML,FAILED"+e1.getMessage() + "\n", jobLogFile, bForceLogFiles);
diff --git a/vcell-client/src/main/java/cbit/vcell/client/BNGWindowManager.java b/vcell-client/src/main/java/cbit/vcell/client/BNGWindowManager.java
index ccca01960..11304e2f0 100644
--- a/vcell-client/src/main/java/cbit/vcell/client/BNGWindowManager.java
+++ b/vcell-client/src/main/java/cbit/vcell/client/BNGWindowManager.java
@@ -177,7 +177,7 @@ public void importSbml(String bngSbmlStr) {
 		for (ReactionStep rs : modifiedBiomodel.getModel().getReactionSteps()) {
 			Kinetics kinetics = rs.getKinetics();
 			if (kinetics instanceof LumpedKinetics) {
-				rs.setKinetics(DistributedKinetics.toDistributedKinetics((LumpedKinetics)kinetics));
+				rs.setKinetics(DistributedKinetics.toDistributedKinetics((LumpedKinetics)kinetics, false));
 			}
 		}
 
diff --git a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/ReactionPropertiesPanel.java b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/ReactionPropertiesPanel.java
index 5dbfad31a..3bd7be0d1 100644
--- a/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/ReactionPropertiesPanel.java
+++ b/vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/ReactionPropertiesPanel.java
@@ -618,7 +618,7 @@ private void updateKineticChoice() {
 	try {
 		Kinetics newKinetics = null;
 		if (bLumpedToGeneral) {
-			newKinetics = DistributedKinetics.toDistributedKinetics((LumpedKinetics)reactionStep.getKinetics());
+			newKinetics = DistributedKinetics.toDistributedKinetics((LumpedKinetics)reactionStep.getKinetics(), true);
 		} else if (bGeneralToLumped) {
 			newKinetics = LumpedKinetics.toLumpedKinetics((DistributedKinetics)reactionStep.getKinetics());
 		} else {
@@ -724,7 +724,7 @@ private JButton getJToggleButton() {
 					}
  				}else if (kinetics instanceof LumpedKinetics){
 					try {
-						reactionStep.setKinetics(DistributedKinetics.toDistributedKinetics((LumpedKinetics)kinetics));
+						reactionStep.setKinetics(DistributedKinetics.toDistributedKinetics((LumpedKinetics)kinetics, true));
 					} catch (Exception e2){
 						e2.printStackTrace(System.out);
 						if (kinetics.getKineticsDescription().isElectrical()){
diff --git a/vcell-client/src/main/java/cbit/vcell/model/gui/KineticsTypeTemplatePanel.java b/vcell-client/src/main/java/cbit/vcell/model/gui/KineticsTypeTemplatePanel.java
index 5c80e6777..3abbd2e8b 100644
--- a/vcell-client/src/main/java/cbit/vcell/model/gui/KineticsTypeTemplatePanel.java
+++ b/vcell-client/src/main/java/cbit/vcell/model/gui/KineticsTypeTemplatePanel.java
@@ -422,7 +422,7 @@ private JButton getJToggleButton() {
 					}
  				}else if (getKinetics() instanceof LumpedKinetics){
 					try {
-						reactionStep.setKinetics(DistributedKinetics.toDistributedKinetics((LumpedKinetics)getKinetics()));
+						reactionStep.setKinetics(DistributedKinetics.toDistributedKinetics((LumpedKinetics)getKinetics(), true));
 					} catch (Exception e2){
 						e2.printStackTrace(System.out);
 						if (getKinetics().getKineticsDescription().isElectrical()){
diff --git a/vcell-core/src/main/java/cbit/util/xml/XmlUtil.java b/vcell-core/src/main/java/cbit/util/xml/XmlUtil.java
index 14bff9c67..1813f08ec 100644
--- a/vcell-core/src/main/java/cbit/util/xml/XmlUtil.java
+++ b/vcell-core/src/main/java/cbit/util/xml/XmlUtil.java
@@ -325,6 +325,7 @@ public static org.jdom.Element setDefaultNamespace(org.jdom.Element rootNode, or
 	public static String xmlToString(Document xmlDoc, boolean bTrimAllWhiteSpace) {
 		XMLOutputter xmlOut = new XMLOutputter();
 		// default newline in jdom 1.1.3 is '\r\n' : xmlOut.setNewlines(true);
+		xmlOut.setFormat(Format.getPrettyFormat());
 	    if (bTrimAllWhiteSpace) {
 	    	xmlOut.getFormat().setTextMode(Format.TextMode.TRIM_FULL_WHITE);
 	    } 
diff --git a/vcell-core/src/main/java/cbit/vcell/math/MathUtilities.java b/vcell-core/src/main/java/cbit/vcell/math/MathUtilities.java
index 4b6f4a53c..83345f44b 100644
--- a/vcell-core/src/main/java/cbit/vcell/math/MathUtilities.java
+++ b/vcell-core/src/main/java/cbit/vcell/math/MathUtilities.java
@@ -199,9 +199,14 @@ public static Expression substituteFunctions(Expression exp, SymbolTable symbolT
  * @exception java.lang.Exception The exception description.
  */
 public static Expression substituteModelParameters(Expression exp, SymbolTable symbolTable) throws ExpressionException {
+	return substituteModelParameters(exp, symbolTable, true, true);
+}
+
+
+public static Expression substituteModelParameters(Expression exp, SymbolTable symbolTable, boolean bToNumbers, boolean bBind) throws ExpressionException {
 	Expression exp2 = new Expression(exp);
 	//
-	// do until no more functions to substitute
+	// do until no more functions to substitute; optionally stop last step before numeric
 	//
 	int count = 0;
 	boolean bSubstituted = true;
@@ -217,14 +222,16 @@ public static Expression substituteModelParameters(Expression exp, SymbolTable s
 				if (ste != null && !(ste instanceof SymbolTableFunctionEntry)) {
 					Expression steExp = ste.getExpression();
 					if (steExp != null) {
-						exp2.substituteInPlace(new Expression(ste.getName()),steExp);
-						bSubstituted = true;
+						if (!((steExp.getSymbols() == null || steExp.getSymbols().length == 0) && !bToNumbers)) {
+							exp2.substituteInPlace(new Expression(ste.getName()),steExp);
+							bSubstituted = true;
+						}
 					}
 				}
 			}
 		}
 	}
-	exp2.bindExpression(symbolTable);
+	if (bBind) exp2.bindExpression(symbolTable);
 	return exp2;
 }
 
diff --git a/vcell-core/src/main/java/cbit/vcell/model/DistributedKinetics.java b/vcell-core/src/main/java/cbit/vcell/model/DistributedKinetics.java
index 9a6a9bb51..842ce7e69 100644
--- a/vcell-core/src/main/java/cbit/vcell/model/DistributedKinetics.java
+++ b/vcell-core/src/main/java/cbit/vcell/model/DistributedKinetics.java
@@ -13,6 +13,10 @@ package cbit.vcell.model;
 import java.beans.PropertyVetoException;
 import java.util.Vector;
 
+import cbit.vcell.math.MathUtilities;
+import cbit.vcell.model.Kinetics.KineticsParameter;
+import cbit.vcell.model.Kinetics.KineticsProxyParameter;
+import cbit.vcell.model.Model.ModelParameter;
 import cbit.vcell.model.Structure.StructureSize;
 import cbit.vcell.parser.Expression;
 import cbit.vcell.parser.ExpressionException;
@@ -189,59 +193,92 @@ public abstract class DistributedKinetics extends Kinetics {
 	}
 
 
-	public static DistributedKinetics toDistributedKinetics(LumpedKinetics origLumpedKinetics){
+	public static DistributedKinetics toDistributedKinetics(LumpedKinetics origLumpedKinetics, boolean bForceScale) throws ExpressionException, PropertyVetoException {
 		KineticsParameter[] origLumpedKineticsParms = origLumpedKinetics.getKineticsParameters();
 		ReactionStep reactionStep = origLumpedKinetics.getReactionStep();
-		try {
-			Vector<KineticsParameter> parmsToAdd = new Vector<KineticsParameter>();
-			
-			
-			DistributedKinetics distributedKinetics = null;
-			StructureSize structureSize = origLumpedKinetics.getReactionStep().getStructure().getStructureSize();
-			KineticsParameter lumpedAuthoritativeParm = origLumpedKinetics.getAuthoritativeParameter();
-			Expression lumpedExpressionWithoutSizeScale = ExpressionUtils.getLinearFactor(lumpedAuthoritativeParm.getExpression(), structureSize.getName());
-			if (lumpedExpressionWithoutSizeScale == null){
-				throw new ExpressionException("linear factor was null, lumped reaction "+origLumpedKinetics.getReactionStep().getName()+" could not be transformed to distributed");
+		Vector<KineticsParameter> parmsToAdd = new Vector<KineticsParameter>();
+		
+		DistributedKinetics distributedKinetics = null;
+		if (origLumpedKinetics.getKineticsDescription().isElectrical()){
+			if (origLumpedKinetics.getReactionStep() instanceof FluxReaction){
+				distributedKinetics = new GeneralCurrentKinetics((FluxReaction)reactionStep);
+			}else if (origLumpedKinetics.getReactionStep() instanceof SimpleReaction){
+				distributedKinetics = new GeneralCurrentKinetics((SimpleReaction)reactionStep);
+			}else{
+				throw new RuntimeException("DistributedKinetics.toDistributedKinetics("+origLumpedKinetics.getReactionStep()+") not supported");
 			}
-			Expression sizeExp = new Expression(structureSize.getName());
-			VCUnitDefinition sizeUnit = structureSize.getUnitDefinition();
-			if (origLumpedKinetics.getKineticsDescription().isElectrical()){
-				if (origLumpedKinetics.getReactionStep() instanceof FluxReaction){
-					distributedKinetics = new GeneralCurrentKinetics((FluxReaction)reactionStep);
-				}else if (origLumpedKinetics.getReactionStep() instanceof SimpleReaction){
-					distributedKinetics = new GeneralCurrentKinetics((SimpleReaction)reactionStep);
-				}else{
-					throw new RuntimeException("DistributedKinetics.toDistributedKinetics("+origLumpedKinetics.getReactionStep()+") not supported");
-				}
+		}else{
+			if (origLumpedKinetics.getReactionStep() instanceof FluxReaction){
+				distributedKinetics = new GeneralKinetics((FluxReaction)reactionStep);
+			}else if (origLumpedKinetics.getReactionStep() instanceof SimpleReaction){
+				distributedKinetics = new GeneralKinetics((SimpleReaction)reactionStep);
 			}else{
-				if (origLumpedKinetics.getReactionStep() instanceof FluxReaction){
-					distributedKinetics = new GeneralKinetics((FluxReaction)reactionStep);
-				}else if (origLumpedKinetics.getReactionStep() instanceof SimpleReaction){
-					distributedKinetics = new GeneralKinetics((SimpleReaction)reactionStep);
-				}else{
-					throw new RuntimeException("DistributedKinetics.toDistributedKinetics("+origLumpedKinetics.getReactionStep()+") not supported");
-				}
+				throw new RuntimeException("DistributedKinetics.toDistributedKinetics("+origLumpedKinetics.getReactionStep()+") not supported");
 			}
-			Expression unitFactor = new Expression(distributedKinetics.getAuthoritativeParameter().getUnitDefinition().multiplyBy(sizeUnit).divideBy(origLumpedKinetics.getAuthoritativeParameter().getUnitDefinition()).getDimensionlessScale());
-			KineticsParameter distAuthoritativeParam = distributedKinetics.getAuthoritativeParameter();
-			Expression newDistributedAuthoritativeExp = Expression.mult(unitFactor,lumpedExpressionWithoutSizeScale).flattenSafe();
-			parmsToAdd.add(distributedKinetics.new KineticsParameter(distAuthoritativeParam.getName(),newDistributedAuthoritativeExp,distAuthoritativeParam.getRole(),distAuthoritativeParam.getUnitDefinition()));
-
-			for (int i = 0; i < origLumpedKineticsParms.length; i++) {
-				if (origLumpedKineticsParms[i].getRole()!=Kinetics.ROLE_LumpedReactionRate &&
-						origLumpedKineticsParms[i].getRole()!=Kinetics.ROLE_LumpedCurrent){
-					parmsToAdd.add(distributedKinetics.new KineticsParameter(origLumpedKineticsParms[i].getName(),new Expression(origLumpedKineticsParms[i].getExpression()),Kinetics.ROLE_UserDefined,origLumpedKineticsParms[i].getUnitDefinition()));
-				}
+		}
+		
+		KineticsParameter distAuthoritativeParam = distributedKinetics.getAuthoritativeParameter();
+
+		// preserve user-defined rate parameter name
+		String origRateParamName = origLumpedKinetics.getAuthoritativeParameter().getName();
+		// need to protect from name conflict between local parameter and names of predefined role parameters in new kinetics
+		reactionStep.setKinetics(origLumpedKinetics);
+		for (int i = 0; i < origLumpedKineticsParms.length; i++) {
+			if (origLumpedKineticsParms[i].getRole()!=Kinetics.ROLE_LumpedReactionRate &&
+					origLumpedKineticsParms[i].getRole()!=Kinetics.ROLE_LumpedCurrent &&
+					origLumpedKineticsParms[i].getName().equals(distAuthoritativeParam.getName())){
+				    origLumpedKineticsParms[i].setName("_"+origLumpedKineticsParms[i].getName()+"_");
 			}
-			distributedKinetics.addKineticsParameters(parmsToAdd.toArray(new KineticsParameter[parmsToAdd.size()]));
-			return distributedKinetics;
-		} catch (PropertyVetoException e) {
-			e.printStackTrace();
-			throw new RuntimeException("failed to create distributed Kinetics for reaction: \""+origLumpedKinetics.getReactionStep().getName()+"\": "+e.getMessage());
-		} catch (ExpressionException e) {
-			e.printStackTrace();
-			throw new RuntimeException("failed to create distributed Kinetics for reaction: \""+origLumpedKinetics.getReactionStep().getName()+"\": "+e.getMessage());
 		}
+		reactionStep.setKinetics(distributedKinetics);
+		origLumpedKineticsParms = origLumpedKinetics.getKineticsParameters(); //if a name change was done above the params were cloned in the process...
+		
+		StructureSize structureSize = origLumpedKinetics.getReactionStep().getStructure().getStructureSize();
+		KineticsParameter lumpedAuthoritativeParm = origLumpedKinetics.getAuthoritativeParameter();
+		Expression sizeExp = new Expression(structureSize.getName());
+		Expression lumpedExpressionWithoutSizeScale = ExpressionUtils.getLinearFactor(lumpedAuthoritativeParm.getExpression(), structureSize.getName());
+		if (lumpedExpressionWithoutSizeScale == null){
+			// try again with model namescope (if it was a global param, prb from SBML import)
+			lumpedExpressionWithoutSizeScale = ExpressionUtils.getLinearFactor(lumpedAuthoritativeParm.getExpression(), new Expression(structureSize, structureSize.getStructure().getModel().getNameScope()).infix());
+		}
+		if (lumpedExpressionWithoutSizeScale == null){
+			// try again with symbol substitution
+			Expression substitutedRate = MathUtilities.substituteModelParameters(lumpedAuthoritativeParm.getExpression(), reactionStep, false, false);
+			lumpedExpressionWithoutSizeScale = ExpressionUtils.getLinearFactor(substitutedRate, new Expression(structureSize, structureSize.getStructure().getModel().getNameScope()).infix());
+		}		
+		if (lumpedExpressionWithoutSizeScale == null){
+			if (bForceScale) {
+				lumpedExpressionWithoutSizeScale = Expression.div(lumpedAuthoritativeParm.getExpression(), sizeExp);
+			} else {
+				throw new ExpressionException("linear factor was null, lumped reaction "+origLumpedKinetics.getReactionStep().getName()+" could not be transformed to distributed");
+			}
+		}
+		VCUnitDefinition sizeUnit = structureSize.getUnitDefinition();
+		Expression unitFactor = new Expression(distributedKinetics.getAuthoritativeParameter().getUnitDefinition().multiplyBy(sizeUnit).divideBy(origLumpedKinetics.getAuthoritativeParameter().getUnitDefinition()).getDimensionlessScale());
+		Expression newDistributedAuthoritativeExp = Expression.mult(unitFactor,lumpedExpressionWithoutSizeScale).flattenSafe();
+		parmsToAdd.add(distributedKinetics.new KineticsParameter(distAuthoritativeParam.getName(),newDistributedAuthoritativeExp,distAuthoritativeParam.getRole(),distAuthoritativeParam.getUnitDefinition()));
+		
+		for (int i = 0; i < origLumpedKineticsParms.length; i++) {
+			if (origLumpedKineticsParms[i].getRole()!=Kinetics.ROLE_LumpedReactionRate &&
+					origLumpedKineticsParms[i].getRole()!=Kinetics.ROLE_LumpedCurrent){
+				KineticsParameter kp = distributedKinetics.new KineticsParameter(origLumpedKineticsParms[i].getName(),new Expression(origLumpedKineticsParms[i].getExpression()),Kinetics.ROLE_UserDefined,origLumpedKineticsParms[i].getUnitDefinition());
+				parmsToAdd.add(kp);
+			}
+		}
+		distributedKinetics.addKineticsParameters(parmsToAdd.toArray(new KineticsParameter[parmsToAdd.size()]));
+		distAuthoritativeParam = distributedKinetics.getAuthoritativeParameter();
+		for (KineticsParameter kp : distributedKinetics.getKineticsParameters()) {
+			if (kp.getRole() == Kinetics.ROLE_UserDefined && distAuthoritativeParam.getExpression().infix().equals(kp.getName())) {
+				distAuthoritativeParam.setExpression(kp.getExpression());
+				break;
+			}
+		}
+		// apply preserved user-defined rate parameter name
+		if (!origRateParamName.equals(origLumpedKinetics.getDefaultParameterName(Kinetics.ROLE_LumpedReactionRate))) {
+			distAuthoritativeParam.setName(origLumpedKinetics.getAuthoritativeParameter().getName());
+		}
+
+		return distributedKinetics;
 	}
 
 }
diff --git a/vcell-core/src/main/java/cbit/vcell/model/Kinetics.java b/vcell-core/src/main/java/cbit/vcell/model/Kinetics.java
index 140916ee9..e2a25b4ed 100644
--- a/vcell-core/src/main/java/cbit/vcell/model/Kinetics.java
+++ b/vcell-core/src/main/java/cbit/vcell/model/Kinetics.java
@@ -53,7 +53,7 @@ java.io.Serializable, IssueSource, Relatable
 
 	public static final String PROPERTY_NAME_KINETICS_PARAMETERS = "kineticsParameters";
 	private String name;
-	private final ReactionStep reactionStep;
+	private ReactionStep reactionStep;
 	protected transient java.beans.PropertyChangeSupport propertyChange;
 	protected transient java.beans.VetoableChangeSupport vetoPropertyChange;
 	private Kinetics.KineticsParameter[] fieldKineticsParameters = new KineticsParameter[0];
@@ -1977,7 +1977,7 @@ public void renameParameter(String oldName, String newName) throws ExpressionExc
 	}
 
 	if (oldName.equals(newName)){
-		throw new RuntimeException("renameParameter from '"+oldName+"' to '"+newName+"', same name not allowed");
+		logger.info("rename parameter called with same name: "+oldName);
 	}
 	KineticsParameter parameter = getKineticsParameter(oldName);
 	if (parameter!=null){
@@ -2451,4 +2451,9 @@ private void onProxyParameterNameChange(String oldName, String newName) throws P
 	}
 }
 
+
+public void setReactionStep(ReactionStep reactionStep) {
+	this.reactionStep = reactionStep;
+}
+
 }
diff --git a/vcell-core/src/main/java/cbit/vcell/model/LumpedKinetics.java b/vcell-core/src/main/java/cbit/vcell/model/LumpedKinetics.java
index 382e8b207..5b604893b 100644
--- a/vcell-core/src/main/java/cbit/vcell/model/LumpedKinetics.java
+++ b/vcell-core/src/main/java/cbit/vcell/model/LumpedKinetics.java
@@ -134,9 +134,6 @@ public abstract class LumpedKinetics extends Kinetics {
 			Vector<KineticsParameter> parmsToAdd = new Vector<KineticsParameter>();
 			
 			LumpedKinetics lumpedKinetics = null;
-			StructureSize structureSize = distributedKinetics.getReactionStep().getStructure().getStructureSize();
-			Expression sizeExp = new Expression(structureSize.getName());
-			VCUnitDefinition sizeUnit = structureSize.getUnitDefinition();
 			if (distributedKinetics.getKineticsDescription().isElectrical()){
 				if (reactionStep instanceof FluxReaction){
 					lumpedKinetics = new GeneralCurrentLumpedKinetics((FluxReaction)reactionStep);
@@ -154,10 +151,29 @@ public abstract class LumpedKinetics extends Kinetics {
 					throw new RuntimeException("LumpedKinetics.toLumpedKinetics("+reactionStep.getClass().getSimpleName()+") not supported");
 				}
 			}
+
+			KineticsParameter lumpedAuthoritativeParam = lumpedKinetics.getAuthoritativeParameter();
+
+			// preserve user-defined rate parameter name
+			String origRateParamName = distributedKinetics.getAuthoritativeParameter().getName();
+			// need to protect from name conflict between local parameter and names of predefined role parameters in new kinetics
+			reactionStep.setKinetics(distributedKinetics);
+			for (int i = 0; i < distKineticsParms.length; i++) {
+				if (distKineticsParms[i].getRole()!=Kinetics.ROLE_ReactionRate &&
+						distKineticsParms[i].getRole()!=Kinetics.ROLE_CurrentDensity &&
+						distKineticsParms[i].getName().equals(lumpedAuthoritativeParam.getName())){
+					    distKineticsParms[i].setName("_"+distKineticsParms[i].getName()+"_");
+				}
+			}
+			reactionStep.setKinetics(lumpedKinetics);
+			distKineticsParms = distributedKinetics.getKineticsParameters(); //if a name change was done above the params were cloned in the process...
+			
+			StructureSize structureSize = distributedKinetics.getReactionStep().getStructure().getStructureSize();
+			Expression sizeExp = new Expression(structureSize.getName());
+			VCUnitDefinition sizeUnit = structureSize.getUnitDefinition();
 			Expression unitFactor = new Expression(lumpedKinetics.getAuthoritativeParameter().getUnitDefinition().divideBy(sizeUnit).divideBy(distributedKinetics.getAuthoritativeParameter().getUnitDefinition()).getDimensionlessScale());
 			Expression lumpingFactor = Expression.mult(sizeExp, unitFactor);
 			KineticsParameter distAuthoritativeParam = distributedKinetics.getAuthoritativeParameter();
-			KineticsParameter lumpedAuthoritativeParam = lumpedKinetics.getAuthoritativeParameter();
 			Expression newLumpedAuthoritativeExp = Expression.mult(lumpingFactor,distAuthoritativeParam.getExpression()).flatten();
 			Expression substitutedExp = newLumpedAuthoritativeExp.getSubstitutedExpression(sizeExp, new Expression(1.0));
 			if (ExpressionUtils.functionallyEquivalent(newLumpedAuthoritativeExp,substitutedExp,false)){
@@ -172,6 +188,12 @@ public abstract class LumpedKinetics extends Kinetics {
 				}
 			}
 			lumpedKinetics.addKineticsParameters(parmsToAdd.toArray(new KineticsParameter[parmsToAdd.size()]));
+			lumpedAuthoritativeParam = lumpedKinetics.getAuthoritativeParameter();
+			// apply preserved user-defined rate parameter name
+			if (!origRateParamName.equals(distributedKinetics.getDefaultParameterName(Kinetics.ROLE_ReactionRate))) {
+				lumpedAuthoritativeParam.setName(distributedKinetics.getAuthoritativeParameter().getName());
+			}
+
 			return lumpedKinetics;
 		} catch (PropertyVetoException e) {
 			e.printStackTrace();
diff --git a/vcell-core/src/main/java/cbit/vcell/model/Parameter.java b/vcell-core/src/main/java/cbit/vcell/model/Parameter.java
index b9b32b592..11cb67a81 100644
--- a/vcell-core/src/main/java/cbit/vcell/model/Parameter.java
+++ b/vcell-core/src/main/java/cbit/vcell/model/Parameter.java
@@ -41,6 +41,7 @@ protected Parameter () {
  * The addPropertyChangeListener method was generated to support the propertyChange field.
  */
 public synchronized void addPropertyChangeListener(java.beans.PropertyChangeListener listener) {
+	PropertyChangeListenerProxyVCell.removeProxyListener(getPropertyChange(), listener);
 	PropertyChangeListenerProxyVCell.addProxyListener(getPropertyChange(), listener);
 }
 /**
diff --git a/vcell-core/src/main/java/cbit/vcell/model/ReactionStep.java b/vcell-core/src/main/java/cbit/vcell/model/ReactionStep.java
index 6dae5a9a4..c155d5a28 100644
--- a/vcell-core/src/main/java/cbit/vcell/model/ReactionStep.java
+++ b/vcell-core/src/main/java/cbit/vcell/model/ReactionStep.java
@@ -433,6 +433,9 @@ public SymbolTableEntry getEntry(String identifier) {
 	if (externalSTE instanceof SymbolTableFunctionEntry){
 		return externalSTE;
 	}
+	if (externalSTE instanceof StructureSize) {
+		return externalSTE;
+	}
 	//
 	// external ste is null and found unresolved parameter, then return unresolved parameter.  
 	// (external entry overrides unresolved parameter).
diff --git a/vcell-core/src/main/java/cbit/vcell/xml/XMLTags.java b/vcell-core/src/main/java/cbit/vcell/xml/XMLTags.java
index 70bd56ae6..f3b6ba9e9 100644
--- a/vcell-core/src/main/java/cbit/vcell/xml/XMLTags.java
+++ b/vcell-core/src/main/java/cbit/vcell/xml/XMLTags.java
@@ -380,6 +380,10 @@ public class XMLTags {
     public final static String SBML_VCELL_OutputFunctionTag = "OutputFunction";
     public final static String SBML_VCELL_OutputFunctionTag_varTypeAttr = "VariableType";
     public final static String SBML_VCELL_OutputFunctionTag_domainAttr = "Domain";
+    // to properly roundtrip renamed/rescaled authoritative rate parameters
+    public final static String SBML_VCELL_RateParamTag = "RateParameter";
+    public final static String SBML_VCELL_RateParamTag_parRoleAttr = "ParameterRole";
+    public final static String SBML_VCELL_RateParamTag_rxIDAttr = "ReactionID";
     // I erased the tag of Unsteadytag
     public final static String SteadyTag = "Steady";
     //I erased the tag of Steadytag
diff --git a/vcell-core/src/main/java/org/jlibsedml/modelsupport/SBMLSupport.java b/vcell-core/src/main/java/org/jlibsedml/modelsupport/SBMLSupport.java
index a5db1d79d..de6858467 100644
--- a/vcell-core/src/main/java/org/jlibsedml/modelsupport/SBMLSupport.java
+++ b/vcell-core/src/main/java/org/jlibsedml/modelsupport/SBMLSupport.java
@@ -253,6 +253,11 @@ public class SBMLSupport implements IXPathToVariableIDResolver {
                 + "/sbml:kineticLaw/sbml:listOfParameters";
     }
 
+    String getXPathForListOfKineticLawParametersV3(String reactionID) {
+        return getXPathForReaction(reactionID)
+                + "/sbml:kineticLaw/sbml:listOfLocalParameters";
+    }
+
     /**
      * Gets XPath expression to identify a global SBML reaction.
      * 
@@ -299,6 +304,23 @@ public class SBMLSupport implements IXPathToVariableIDResolver {
                 + "/sbml:parameter[@id='" + parameterID + "']";
     }
 
+    /**
+     * Gets XPath expression to identify a local reaction kineticLaw parameter.
+     * 
+     * @param reactionID
+     *            The reaction Id.
+     * @param parameterID
+     *            The localParameter Id.
+     * @return An XPath string which can be set into the 'target' field of an
+     *         XPath-containing element in SED-ML.
+     */
+    public String getXPathForKineticLawParameterV3(String reactionID,
+            String parameterID) {
+        return getXPathForListOfKineticLawParameters(reactionID)
+                + "/sbml:localParameter[@id='" + parameterID + "']";
+    }
+    
+    
     /**
      * Gets XPath expression to identify an attribute of a local reaction
      * kineticLaw parameter.
diff --git a/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLExporter.java b/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLExporter.java
index bba0cceb5..2e96da9f9 100644
--- a/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLExporter.java
+++ b/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLExporter.java
@@ -39,6 +39,7 @@ import cbit.vcell.mapping.spatial.SpatialObject.QuantityComponent;
 import cbit.vcell.mapping.spatial.SpatialObject.SpatialQuantity;
 import cbit.vcell.mapping.spatial.processes.SpatialProcess;
 import cbit.vcell.math.*;
+import cbit.vcell.math.Variable;
 import cbit.vcell.matrix.MatrixException;
 import cbit.vcell.model.*;
 import cbit.vcell.model.Model;
@@ -675,8 +676,8 @@ private void addReactions() throws SbmlException, XMLStreamException {
 		org.sbml.jsbml.KineticLaw sbmlKLaw = sbmlReaction.createKineticLaw();
 
 		try {
-			final Expression localRateExpr;
-			final Expression lumpedRateExpr;
+			Expression localRateExpr;
+			Expression lumpedRateExpr;
 			if (vcRxnKinetics instanceof DistributedKinetics){
 				Expression temp_localRateExpr = ((DistributedKinetics) vcRxnKinetics).getReactionRateParameter().getExpression();
 				if (temp_localRateExpr != null){
@@ -866,17 +867,34 @@ private void addReactions() throws SbmlException, XMLStreamException {
 
 			// After making all necessary adjustments to the rate expression, now set the sbmlKLaw.
 			final ASTNode exprFormulaNode;
-			if (lumpedRateExpr!=null){
-				exprFormulaNode = getFormulaFromExpression(lumpedRateExpr);
-			}else{
-				if (bSpatial){
-					exprFormulaNode = getFormulaFromExpression(localRateExpr);
-				}else{
-					String structure = vcReactionStep.getStructure().getName();
-					structure = symbolTableEntryNameToSidMap.get(structure);
-					exprFormulaNode = getFormulaFromExpression(Expression.mult(localRateExpr, new Expression(structure)));
-				}
-			}
+			Expression rateExpr = lumpedRateExpr != null ? lumpedRateExpr : localRateExpr;
+			if (lumpedRateExpr == null && !bSpatial) rateExpr = Expression.mult(rateExpr, new Expression(symbolTableEntryNameToSidMap.get(vcReactionStep.getStructure().getName())));
+			KineticsParameter rateParam = vcRxnKinetics.getAuthoritativeParameter();
+			MathSymbolMapping msm = (MathSymbolMapping)vcSelectedSimContext.getMathDescription().getSourceSymbolMapping();
+			Variable var = msm.getVariable(rateParam);
+			if (var != null && var.isConstant()) {
+				// make global parameter aith expression of rate parameter
+				// create assignment rule for it
+				// mark the global parameter as proxy for the rate parameter 
+				String newParamName = TokenMangler.mangleToSName(rateParam.getName() + "_" + vcReactionStep.getName());
+				Pair<String, String> origParam = new Pair<String, String> (rxnName, rateParam.getName());
+				l2gMap.put(origParam, newParamName);	
+				ASTNode paramFormulaNode = getFormulaFromExpression(rateExpr);
+				AssignmentRule sbmlParamAssignmentRule = sbmlModel.createAssignmentRule();
+				sbmlParamAssignmentRule.setVariable(newParamName);
+				sbmlParamAssignmentRule.setMath(paramFormulaNode);
+				org.sbml.jsbml.Parameter sbmlKinParam = sbmlModel.createParameter();
+				sbmlKinParam.setId(newParamName);
+				sbmlKinParam.setName(rateParam.getName());
+				sbmlKinParam.setUnits(getOrCreateSBMLUnit(rateParam.getUnitDefinition()));
+				sbmlKinParam.setConstant(false);
+				Element rateParamElement = new Element(XMLTags.SBML_VCELL_RateParamTag, sbml_vcml_ns);
+				rateParamElement.setAttribute(XMLTags.SBML_VCELL_RateParamTag_parRoleAttr, Integer.toString(rateParam.getRole()), sbml_vcml_ns);
+				rateParamElement.setAttribute(XMLTags.SBML_VCELL_RateParamTag_rxIDAttr, sbmlReactionId, sbml_vcml_ns);
+				sbmlKinParam.getAnnotation().appendNonRDFAnnotation(XmlUtil.xmlToString(rateParamElement));
+				rateExpr = new Expression(newParamName);
+			}
+			exprFormulaNode = getFormulaFromExpression(rateExpr);
 			sbmlKLaw.setMath(exprFormulaNode);
 		} catch (cbit.vcell.parser.ExpressionException e) {
 			e.printStackTrace(System.out);
diff --git a/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLImporter.java b/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLImporter.java
index eb61b1328..bc95494a9 100644
--- a/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLImporter.java
+++ b/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLImporter.java
@@ -1797,10 +1797,19 @@ public class SBMLImporter {
 		try {
 			for (ReactionStep reactionStep : vcBioModel.getModel().getReactionSteps()) {
 				if (reactionStep.getKinetics().getKineticsDescription().isLumped()) {
+					Kinetics origKinetics = reactionStep.getKinetics();
+					// clone it for backup purposes
+					origKinetics.setReactionStep(null);
+					Kinetics clonedKinetics = (Kinetics)BeanUtils.cloneSerializable(origKinetics);
+					origKinetics.setReactionStep(reactionStep);
 					try {
-						DistributedKinetics.toDistributedKinetics((LumpedKinetics) reactionStep.getKinetics());
+						DistributedKinetics.toDistributedKinetics((LumpedKinetics) origKinetics, false);
 					}catch (Exception e){
 						logger.warn("failed to transform lumped reaction "+reactionStep.getName()+" to distributed: "+e.getMessage(),e);
+						// original kinetics may have been altered when the conversion failed, replace with clone
+						reactionStep.setKinetics(clonedKinetics);
+						clonedKinetics.setReactionStep(reactionStep);
+						reactionStep.refreshDependencies();
 					}
 				}
 			}
@@ -2754,6 +2763,8 @@ public class SBMLImporter {
 		// TODO: use BMDB model Whitcomb to test comp size initialized with expression
 		finalizeCompartments(sbmlModel, vcBioModel, sbmlSymbolMapping, localIssueList, issueContext);
 
+		finalizeReactions(sbmlModel, vcBioModel, sbmlSymbolMapping);
+
 		try {
 			if (!bSpatial) {
 				StructureSizeSolver.updateRelativeStructureSizes(vcBioModel.getSimulationContext(0));
@@ -2791,6 +2802,31 @@ public class SBMLImporter {
 		addOutputFunctions(sbmlModel, vcBioModel, sbmlSymbolMapping, vcLogger);
 	}
 
+	private static void finalizeReactions(org.sbml.jsbml.Model sbmlModel, BioModel vcBioModel,
+			SBMLSymbolMapping sbmlSymbolMapping) throws PropertyVetoException {
+
+		for (int i = 0; i < vcBioModel.getModel().getReactionSteps().length; i++) {
+			KineticsParameter rateParam = vcBioModel.getModel().getReactionSteps(i).getKinetics().getAuthoritativeParameter();
+			String[] rateSymbols = rateParam.getExpression().getSymbols();
+			if (rateSymbols != null && rateSymbols.length == 1) {
+				String id = rateSymbols[0];
+				SBase sbase = sbmlSymbolMapping.getMappedSBase(id);
+				Annotation paramAnnotation = sbase.getAnnotation();
+				if (paramAnnotation.getNonRDFannotation() != null) {
+					XMLNode paramElement = paramAnnotation.getNonRDFannotation()
+							.getChildElement(XMLTags.SBML_VCELL_RateParamTag, "*");
+					if (paramElement != null) {
+						ModelParameter globalParam = (ModelParameter) sbmlSymbolMapping.getRuntimeSte(sbase);
+						rateParam.setExpression(globalParam.getExpression());
+						vcBioModel.getModel().removeModelParameter(globalParam);
+						rateParam.setName(sbase.getName());
+						sbmlSymbolMapping.replaceRuntime(sbase, rateParam);
+					} 
+				}
+			} 
+		}
+		
+	}
 	private static void addOutputFunctions(org.sbml.jsbml.Model sbmlModel, BioModel vcBioModel, SBMLSymbolMapping sbmlSymbolMapping,
 			VCLogger vcLogger) throws VCLoggerException {
 
diff --git a/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLSymbolMapping.java b/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLSymbolMapping.java
index a6e891351..f51462311 100644
--- a/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLSymbolMapping.java
+++ b/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLSymbolMapping.java
@@ -122,6 +122,16 @@ public class SBMLSymbolMapping {
         }
     }
 
+    void replaceRuntime(SBase _sbase, EditableSymbolTableEntry ste) {
+        SBaseWrapper<SBase> sbaseWrapper = new SBaseWrapper<>(_sbase);
+        EditableSymbolTableEntry s = sbase_to_runtime_ste_map.get(sbaseWrapper);
+        if (s == null) {
+            throw new RuntimeException("calling replaceRuntime for sbmlSid " + _sbase.getId() + " not mapped prior, trying to replace with " + ste.getClass().getSimpleName()+"("+s.getName()+"), trying to rebind to " + ste.getClass().getSimpleName()+"("+ste.getName()+")");
+        } else {
+            sbase_to_runtime_ste_map.put(sbaseWrapper, ste);
+        }
+    }
+
     Set<SBaseWrapper<SBase>> getAllSbaseWrappers() {
         LinkedHashSet<SBaseWrapper<SBase>> allInitialSBase = new LinkedHashSet<>(sbase_to_initial_ste_map.keySet());
         LinkedHashSet<SBaseWrapper<SBase>> allRuntimeSBase = new LinkedHashSet<>(sbase_to_runtime_ste_map.keySet());
diff --git a/vcell-core/src/main/java/org/vcell/sedml/SEDMLExporter.java b/vcell-core/src/main/java/org/vcell/sedml/SEDMLExporter.java
index 7257d299e..e1fff3394 100644
--- a/vcell-core/src/main/java/org/vcell/sedml/SEDMLExporter.java
+++ b/vcell-core/src/main/java/org/vcell/sedml/SEDMLExporter.java
@@ -410,7 +410,7 @@ public class SEDMLExporter {
 				// 3 ------->
 				// create Tasks
 				Set<String> dataGeneratorTasksSet = new LinkedHashSet<>();	// tasks not referenced as subtasks by any other (repeated) task; only these will have data generators
-				MathOverrides mathOverrides = new MathOverrides(vcSimulation, vcSimulation.getMathOverrides()); // need to clone so we can manipulate expressions
+				MathOverrides mathOverrides = vcSimulation.getMathOverrides(); // need to clone so we can manipulate expressions
 				createSEDMLtasks(simContextCnt, l2gMap, simContextName, simContextId, mathSymbolMapping,
 						vcSimulation, utcSim, dataGeneratorTasksSet, mathOverrides);
 				
@@ -776,6 +776,7 @@ public class SEDMLExporter {
 				for (String unscannedParamName : unscannedParamHash.values()) {
 					SymbolTableEntry ste = getSymbolTableEntryForModelEntity(mathSymbolMapping, unscannedParamName);
 					Expression unscannedParamExpr = mathOverrides.getActualExpression(unscannedParamName, 0);
+					unscannedParamExpr = adjustIfRateParam(vcSimulation, ste, unscannedParamExpr);
 					if(unscannedParamExpr.isNumeric()) {
 						// if expression is numeric, add ChangeAttribute to model created above
 						XPathTarget targetXpath = getTargetAttributeXPath(ste, l2gMap);
@@ -878,6 +879,7 @@ public class SEDMLExporter {
 				for (String unscannedParamName : unscannedParamHash.values()) {
 					SymbolTableEntry ste = getSymbolTableEntryForModelEntity(mathSymbolMapping, unscannedParamName);
 					Expression unscannedParamExpr = mathOverrides.getActualExpression(unscannedParamName, 0);
+					unscannedParamExpr = adjustIfRateParam(vcSimulation, ste, unscannedParamExpr);
 					if(unscannedParamExpr.isNumeric()) {
 						// if expression is numeric, add ChangeAttribute to model created above
 						XPathTarget targetXpath = getTargetAttributeXPath(ste, l2gMap);
@@ -958,11 +960,28 @@ public class SEDMLExporter {
 		}
 	}
 
+	private Expression adjustIfRateParam(Simulation vcSimulation, SymbolTableEntry ste, Expression unscannedParamExpr)
+			throws ExpressionException {
+		if (ste instanceof KineticsParameter) {
+			KineticsParameter kp = (KineticsParameter)ste;
+			if (kp.getKinetics().getAuthoritativeParameter() == kp) {
+				boolean bSpatial = vcSimulation.getSimulationOwner().getGeometry().getDimension() > 0;
+				boolean bLumped = kp.getKinetics() instanceof LumpedKinetics;
+				if (!bLumped && !bSpatial) {
+					MathSymbolMapping msm = (MathSymbolMapping)vcSimulation.getSimulationOwner().getMathDescription().getSourceSymbolMapping();
+					cbit.vcell.math.Variable structSize = msm.getVariable(kp.getKinetics().getReactionStep().getStructure().getStructureSize());
+					unscannedParamExpr = Expression.mult(unscannedParamExpr, new Expression(structSize.getName()));
+				}
+			}
+		}
+		return unscannedParamExpr;
+	}
+
 	private RepeatedTask createSEDMLreptask(String rangeId, Map<Pair<String, String>, String> l2gMap,
 			MathSymbolMapping mathSymbolMapping, Set<String> dataGeneratorTasksSet,
 			MathOverrides mathOverrides, String ownerTaskId, String scannedConstName, String repeatedTaskId, String modelReferenceId)
 			throws ExpressionException, DivideByZeroException, MappingException {
-		RepeatedTask rt = new RepeatedTask(repeatedTaskId, repeatedTaskId, true, rangeId);
+		RepeatedTask rt = new RepeatedTask(repeatedTaskId, mathOverrides.getSimulation().getName(), true, rangeId);
 		dataGeneratorTasksSet.add(rt.getId());
 		SubTask subTask = new SubTask("0", ownerTaskId);
 		dataGeneratorTasksSet.remove(ownerTaskId);
@@ -1191,7 +1210,7 @@ public class SEDMLExporter {
 			Pair<String, String> key = new Pair(reactionID, parameterID);
 			String value = l2gMap.get(key);
 			if(value == null) {
-				targetXpath = new XPathTarget(sbmlSupport.getXPathForKineticLawParameter(reactionID, parameterID));
+				targetXpath = new XPathTarget(sbmlSupport.getXPathForKineticLawParameterV3(reactionID, parameterID));
 			} else {
 				targetXpath = new XPathTarget(sbmlSupport.getXPathForGlobalParameter(value, ParameterAttribute.value));
 			}
diff --git a/vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java b/vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java
index 28ab9b6e0..c10a3a24a 100644
--- a/vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java
+++ b/vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java
@@ -313,12 +313,6 @@ public class SEDMLImporter {
 			for (BioModel bm : uniqueBioModelsList) {
 				BioModel vcbm = null;
 				// we should not fail if any of these steps don't succeed
-				try {
-					ModelUnitSystem vcUnits = ModelUnitSystem.createDefaultVCModelUnitSystem();
-					vcbm = ModelUnitConverter.createBioModelWithNewUnitSystem(bm, vcUnits);
-				} catch (Exception e1) {
-					logger.info("Failed to convert unit system to VCell units: " + e1);
-				}
 				try {
 				// cannot do this for now, as it can be very expensive (hours!!)
 				// also has serious memory issues (runs out of memory even with bumping up to Xmx12G
@@ -460,7 +454,7 @@ public class SEDMLImporter {
 				Expression exp = null;
 				if (change.isChangeAttribute()) {
 					exp = new Expression(((ChangeAttribute)change).getNewValue());
-				} else if (change.isComputeChange()) {
+				} else if (change.isComputeChange() || change.isSetValue()) {
 					ComputeChange cc = (ComputeChange)change;
 					ASTNode math = cc.getMath();
 					exp = new ExpressionMathMLParser(null).fromMathML(math, "t");
@@ -681,12 +675,42 @@ public class SEDMLImporter {
 	private Map<String, BioModel> createBioModels(List<org.jlibsedml.Model> mmm) throws RuntimeException {
 		// first go through models without changes which are unique and must be imported as new BioModel/SimContext
 		Map<String, BioModel> bmMap = new HashMap<>();
+		List<Model> modelsWithoutChanges = new ArrayList<Model>();
 		for(Model mm : mmm) {
 			if (mm.getListOfChanges().isEmpty()) {
-				String id = mm.getId();
-				bmMap.put(id, importModel(mm));
+				modelsWithoutChanges.add(mm);
 			}
 		}
+		if (!modelsWithoutChanges.isEmpty()) {
+			boolean needMoreImport = false;
+			do {
+				for (Model mm : modelsWithoutChanges) {
+					// check first whether it is simply a reference to another model within this sedml, so we don't duplicate
+					String refID = null;
+					if (mm.getSource().startsWith("#")) {
+						refID = mm.getSource().substring(1);
+						// direct reference within sedml
+					} else {
+						// need to check if it is an indirect reference (another model using same source URI)
+						for (Model model : sedml.getModels()) {
+							if (model != mm && model.getSource().equals(mm.getSource())) {
+								refID = model.getId();
+							}
+						}
+					}
+					if (refID == null) {
+						bmMap.put(mm.getId(), importModel(mm));
+					} else {
+						BioModel refBM = bmMap.get(refID);
+						if (refBM != null) {
+							bmMap.put(mm.getId(), refBM);
+						} else {
+							needMoreImport = true;
+						}
+					}
+				} 
+			} while (needMoreImport);
+		}
 		// now go through models with changes and see if they refer to another model within this sedml
 		for(Model mm : mmm) {
 			if (!mm.getListOfChanges().isEmpty()) {
['vcell-core/src/main/java/cbit/vcell/math/MathUtilities.java', 'vcell-core/src/main/java/cbit/util/xml/XmlUtil.java', 'vcell-cli/src/main/java/org/vcell/cli/vcml/VcmlOmexConverter.java', 'vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java', 'vcell-core/src/main/java/org/vcell/sedml/SEDMLExporter.java', 'vcell-core/src/main/java/cbit/vcell/model/LumpedKinetics.java', 'vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLSymbolMapping.java', 'vcell-core/src/main/java/cbit/vcell/model/ReactionStep.java', 'vcell-client/src/main/java/cbit/vcell/model/gui/KineticsTypeTemplatePanel.java', 'vcell-client/src/main/java/cbit/vcell/client/BNGWindowManager.java', 'vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLExporter.java', 'vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLImporter.java', 'vcell-core/src/main/java/cbit/vcell/model/Parameter.java', 'vcell-client/src/main/java/cbit/vcell/client/desktop/biomodel/ReactionPropertiesPanel.java', 'vcell-core/src/main/java/cbit/vcell/model/Kinetics.java', 'vcell-core/src/main/java/cbit/vcell/model/DistributedKinetics.java', '.github/workflows/create_bs_singularity.yml', 'vcell-core/src/main/java/cbit/vcell/xml/XMLTags.java', 'vcell-core/src/main/java/org/jlibsedml/modelsupport/SBMLSupport.java']