Order of execution error: Write and read data in Imperative flow

Hi!

As this is my first topic, before I start i would like to thank all the people on this forum for the help they have provided me learning Dynamo, even though I’ve never been part of their conversations. I am just a beginner and have found this forum very helpful!

I was working on an experiment where is would like to start on generative design. (Withouth Autodesk Refinery) To do this I was planning to itterate over different variations of a design and then read some results of that design to pick the best option. As this will obviously need to be done in the correct sequence I looked at Imperative designscript to get me where I need to be, unfortunately I do not get it to work for even a simple example as I always read the data from before the change is made. The result is that i can only read the correct data when i Run twice.

I’ve reduced the sample to the following situation: My model contains a single wall which length is dependant on the global parameter “Width”. After Dynamo changed the parameter I would like to read the Area of the wall. (see image)


Now I was trying to change the global Parameter based on a number slider, which works fine. Then I would like to select the walls and read the new area. Here I hit a bumb: I can’t seem to find a way to select the walls using designscript, so I’ve used a function block instead. Reading the data from that point on does not seem to always give me the updated data.

More specifically; when I am using Element.GetParameterValueByName() to get the data I won’t get the latest results. But when I am using Revit.Elements.Parameter.ParameterByName() I will get the latest result! (Yay!) Unfortunately the latter gives me a value with units, which prevents me from doing some basic math.(Which i will need later in the design)

I am not sure what the issue is with the first function but it might have to do with the selection outside of the imperative scope? Is there a method to create a selection within the imperative scope?

Alternatively; would it be possible to remove the units from the value of the second function? I have not found a way to remove values and Parameter.Value() does not seem to work in Imperative code?

My script(s):

reqWidth;
[Imperative] {

// Update Global parameter
paramName = GlobalParameter.FindByName("Width");
GlobalParameter.SetValue(paramName, reqWidth);

// Find walls
return = Category.ByName("Walls");
// Return functon as selection cannot be done
// in Designscript.

};

and:

walls;
[Imperative]{
_area = Element.GetParameterValueByName(walls[0], "Area");
return = _area;

//_area_test1 =  Revit.Elements.Parameter.ParameterByName(walls, "Area");
};

Any help would be greatly appreciated!

Kind regards

Try removing any trailing unit markers with a String.Split(value, " ")[0]. To convert from fractional feet/inches there is some good code in Springs package (Feet2Decimal maybe?) which will show you how to do this directly.

Also I don’t see a need to get imperative with this - the prior method of GetParameterValueByName can likely be leveraged directly and let Dynamo’s built in methods for dealing with lacing do the trick.

Out of curiosity, why avoid Refinery? You’ll be limited to one run at a time, won’t be able to leverage multiple cores simultaneously, and will have to sidestep the many issues associated with multiple simultaneous Revit interactions (parameters can only have one value at a time, and sequential setting/getting looping of values is exceptionally slow).

1 Like

Hi Jacob, Thanks so much for your help!

I’m not having any luck with String.Split() yet i’m afraid. Though I understand what you’re saying and think I’ll probably get it to work.

I used the imperative order to make sure I need 1 run to change data and read the new results. I planned to create a loop which would test multiple length values and read the new area. I wrongly assumed that needing just a single run would make it fit for looping.
I’ve tried that now but I’m seeing the same Area result for each length. It looks like it only passes the last value to the model and does not update the database at the end of every for-loop. It also reads only the last wall area value.
I’ve re-ordered the same script, the last run of the loop works fine, the rest doesn’t. I still don’t have a good idea of when Dynamo executes a command.

About Refinery; it looks awesome, I just got lots of java errors when I tried it the first time and then thought it shouldn’t be difficult to create a simple generative design run myself… I’m thinking I might be wrong there, seeing I can’t even measure a single wall’s area. :stuck_out_tongue:

The script:

reqWidth = 1000.0..#10..1000.0;
walls;

[Imperative]{
	areaList = [];
	for (width in reqWidth){
		// Update Global parameter
		param = GlobalParameter.FindByName("Width");
		GlobalParameter.SetValue(param, width);

		// Read wall area's
		_area =  Revit.Elements.Parameter.ParameterByName(walls, "Area");
		areaList = List.AddItemToEnd(["Width = " + width, _area], areaList);
	}

	return = areaList;
};

try using transaction.end after you modify the parameter.

3 Likes

Thanks Michael! That looks much better! I’m definitely seeing Revit change the model to the different variations! Just not getting different area values back, it always returns the area value of the last variation.


I am hopeful but confused by the order of execution. :upside_down_face: I thought it might have to do with the selection of the wall before the loop starts, but then it should give me the area of the first wall-length. It doesn’t however, it’s always the area of the last wall-length.

reqWidth = 1000.0..#10..1000.0;
walls;

[Imperative]{
	areaList = [];
	for (width in reqWidth){
		// Update Global parameter
		param = GlobalParameter.FindByName("Width");
		t1 = GlobalParameter.SetValue(param, width);
		Transaction.End(t1);

		// Read wall area's
		area = Revit.Elements.Parameter.ParameterByName(walls, "Area");
		areaList = List.AddItemToEnd(["Width = " + width, area], areaList);

	}
	return = areaList;
};

It would seem you are grabbing the walls prior to making the change and instigating a end of transaction. Therefore it has the original element in memory and associated data…

Therefore you need to make the change and end the transaction then you get the walls and get the area. Then get it to repeat per width.

I would personally do this in python because then you can use the filterelementcollector to get the walls after the transacation and refreshing the document.

1 Like

Yes! That’s what I initially though as well! In found that it can’t be though after doing some tests with different starting values.

Happy to say i found my error: what was happening is that I’m not passing the parameter’s value to the list but I’m passing the reference to the parameter. The watch-node just translates that to a string which I was using as a reference.

Parameter.Value is not allowed within Imperatives script but JacobSmall already soved this: converting the parameter to a string gives the option to split that string into the correct values, which can then be converted to a number. Yay!

reqWidth = 1000.0..#10..1000.0;
walls;

[Imperative]{
	areaList = [];
	for (width in reqWidth){
		// Update Global parameter
		param = GlobalParameter.FindByName("Width");
		t1 = GlobalParameter.SetValue(param, width);
		Transaction.End(t1);

		// Read wall area's
		_area = Parameter.ParameterByName(walls, "Area");
		_area = String.Split(_area + " ", " ")[2];
		_area = String.ToNumber(_area);
		areaList = List.AddItemToEnd(["Width = " + width, _area], areaList);
	}

	return = areaList;
};

Now quickly putting something awful together to find which wall results in the largest area:


Surprise! It’s the longest wall! Who would have thought?

Thanks everyone for your great help and thinking along! Unfortunately i can only appoint one solution author though it’s definitely a combination of multiple people who fixed the issues with this!

1 Like

Not quite the answer you are probably looking for, but there is also this DesignScript option if you have an external list of UniqueIds :slight_smile: