deleting or removing variant using variant array handle gives unexpected results.

7 views (last 30 days)
If I have a variable that contains a SimBiology parameter object, and I change it, the object reflects that change:
par1 = addparameter(rx1kl,'kf');
>> par1.Value = 0.3
par1 =
struct with fields:
Value: 0.3000
This is expected behavior. If I change an object, I should see that change reflected in the object.
This does not happen with variant arrays. If i create a model mc with several variants, I can a Variant Array object, thus
mcv = mc.Variants;
Supposing I want to delete the second variant in the array, which is named 'Var2'. I should be able to do this in a few ways, with the variant array object.
% Either
delete(mcv(2)); % Method 1, or
removevariant(mc,mcv(2)) % Method 2, or
removevariant(mc,'Var2') % Method 3
However, if afterwards I look at the mcv object, the array is the same size as before the deletion. And Method 1 changes the mcv object from a Simbiology Variant Object to a modified array using handles. That is, if I display the object using
mcv
I get different results than if I refresh the mcv variable:
mcv = mc.Variant
the variant is seen to have been deleted from the model object, but not from the variant object denoted mcv.
So in one case (parameters) modifying the object shows up as expected. In another case(variant arrays) certain modifications do not have the expected outcome and modify the named object ('mcv') in different and unexpected ways. And given that the mcv and mc.Variant objects are different after the delete() and remove() functions I suspect that creating mcv creates a handle to a copy of the object. But the model object itself appears to have been modifed as expected. I don't understand it, anyway.
The workaround is to do an mcv=mc.Variant command after every delete() or removevariant() command. I suspect this is not as intended. What's going on with this? Or am I doing something wrong?
I've created a script to show the behavior
%% Demo weird variant behavior
dm = createnewmodel; % See function below
dcv = dm.Variants; % create a handle for the variant array
disp('Result of removing variant 2 with removevariant() function')
disp('Original Array, using handle:');
dcv % display original variant array
removevariant(dm,dcv(2));
disp('Modified Array, using handle:');
dcv % display original variant array
disp('Not deleted in handle!');
disp('Showing that this removal affects the actual model');
dcv = dm.Variants % display variant array
disp('Result of removing variant 2 with delete() function')
dm2 = createnewmodel;
dcv2 = dm2.Variants;
disp('Another model, original variant array:');
dcv2
delete(dcv2(2)); % use delete to delete variant per documentation
disp('Modified Array, using handle:');
dcv2
disp('Supposedly deleted variant');
dcv2(2)
disp('Showing effect on model')
dcv2=dm2.Variants % only shows array of handles
function dm = createnewmodel();
dm = sbiomodel('demomodel'); % demo model
dc = addcompartment(dm,'democomp'); % add one compartment
dc.Capacity = 5;
dc.CapacityUnits = 'liter';
ds1 = addspecies(dc,'dspec1'); % add two species
ds2 = addspecies(dc,'dspec2');
set(ds1,'InitialAmount',100); % set ICs
set(ds2,'InitialAmount',0);
rx1 = addreaction(dm,'dspec1','dspec2'); % add one reaction
rx1kl = addkineticlaw(rx1,'MassAction');
par1 = addparameter(rx1kl,'kf');
par1.Value = 0.1;
par1.ValueUnits = '1/minute';
v1 = addvariant(dm,'var1'); % add three variants
addcontent(v1,{'species','dspec1','InitialAmount',111})
addcontent(v1,{'parameter','kf','Value',0.2})
v2 = addvariant(dm,'var2');
addcontent(v2,{'species','dspec1','InitialAmount',90})
addcontent(v2,{'parameter','kf','Value',0.3})
v3 = addvariant(dm,'var3');
addcontent(v3,{'species','dspec1','InitialAmount',95})
addcontent(v3,{'parameter','kf','Value',0.23})
addcontent(v3,{'compartment','democomp','Capacity',7})
end

Accepted Answer

Arthur Goldsipe
Arthur Goldsipe on 9 Oct 2019
I know it's confusing, and I'll try to explain what's going on. But let me start by saying that the behavior you're seeing is what we intended, and it's consistent with how handle objects generally behave in MATLAB. The one area where I think SimBiology could be better is that the name removevariant is a bit misleading. Perhaps you should think of it as extractvariant instead.
Here's the bottom line: If you want to delete a variant, then call the delete method. removevariant is intended to extract a variant from a model, making it "standalone," so that if you delete the model it no longer deletes the variant. Once a variant is standalone, it will only get deleted when (1) you explicitly call delete on it or (2) the last reference to it goes away. So the reason that removevariant doesn't delete the variants is because you've stored references to them in variables like mcv. So if you simply clear the variable mcv after calling removevariant, the variant will get deleted. (I can show you a trick for how to "see" that, if you think it would be helpful.)
Likewise, after calling removevariant, mcv is different from mc.Variant because mcv was never in sync with mcv.Variant. Instead, if an object exists in both mcv and in mc.Variant, you can change the object via one, and see the change in the other (because they both refer to the same object). Maybe it would help you see if the difference if you realize that you could just as easily set mcv to just the first variant with mcv=mc.Variants(1), or even to a collection of variants from different models with mcv=[model1.Variants; model2.Variants].
Now, if you delete a variant contained in a SimBiology model, the model is smart enough to remove it from its list of variants. That's not something that happens with "normal" variables. For example, the variant does not get removed from mcv. Instead, that element of the vector shows up as a "handle to deleted Variant." That's pretty standard MATLAB behavior: Deleting a handle object results in a "deleted" object rather than remove it from any list. You normally have to remove it from a vector the way you remove any element from a vector. For example, you can use the special [] syntax: mcv(2) = [].
I think your concern is keeping mcv in sync with mc.Variants. Unforutunately, there's no real way to guarantee that. And the same is true with a doing something like mcp=mc.Parameters. (You alluded to some difference between variants and parameters. But the only difference I can think of is that parameters cannot be standalone, so there's no removeparameter method.)
Without knowing more about what you're trying to do and why you want to keep things in sync, I don't quite know what to recommend, other than to always accessing the property directly via the model if there's any chance it could have gotten modified.
  3 Comments
Jim Bosley
Jim Bosley on 10 Oct 2019
Again, Thanks Arthur. I hope my long verbose comment to your answer was helpful in understanding how at least one user uses variants. Your replies have been really, really helpful to me.

Sign in to comment.

More Answers (0)

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!