Example API Connection Optimization with Templates and Parameters - idea-statica/ideastatica-public GitHub Wiki

Status: 👷‍♂️ Work In Progress

Example Aim

The aim of this example is to provide an insight into how operation parameters can be used to optimize many 'instances' of the same connection type through the use of connection templates and user-defined operation parameters. We will look at using the IDEAStatiCa ConnectionHiddenCheckClient API to iteratively modify connection parameters in order to get to an optimized/desired solution.

In this example, we will look at optimizing multiple weld sizes for a Tube - Tube - Tube Connection as shown below.

tube tube tube example image

Background

IDEA StatiCa allows the ability to control specific operation parameters values through the use of 'user-defined' operation parameters. This enables a simple approach to enable optimization of connection through the manipulation of specific parameters using the IDEAStatiCa Plugin API.

Templates now also can contain and pass these parameters to any Connection file they are applied to. This provides an extreme advantage in the instance where a lot of similar connections share similar operations, although may have slightly different loading or section sizing. Instead of having to create and assign the parameters individually they can automatically be set and defined through the use of a template.

Reference Model and Template

1. Define the Connection Operations

2. Setting Manual Parameters

In this example, we will set parameters for each of the weld thicknesses for the two accompanying welds.

Currently, parameters must be manually set in a connection project.

For information on setting an .ideaCon project parameters please see this guide. The Operation Parameter Reference Guide can also be used to provide information on more complex parameter referencing and generation.

3. Export the Template

Currently, you can export a template when in developer mode (User mode 16).

Use the provided button to export the template to a .ideaConTemp file.

Simple Optimization Procedure

We will now create the simple optimization procedure using c# code and the ConnectionHiddenCheckClient. This is a simple representation of how you can perform this procedure.

The code below has been simplified for the example. It can be modulated to enable class-specific optimization procedures that can be stored and used as required.

1. Read the available parameters from the Template File

This step is only required if the user wants to read and set values of the parameters in an external program without the need to open the Connection Project through the API prior to setting up an optimization procedure.

2. Define the Parameter/s to Optimize

In this example, we set an OptimisationParameter. This is a parameter that will change as we look for the desired solution.

The abstract class OptimizationParameter will store the imported parameter. The constructor allows for the parameter to be created prior to the Connection file being opened by using a SearchName.

When the file is opened and the parameters in the file are extracted a search is performed to ensure the parameter is present in the file prior to continuing. The OptimizationParameter class also serves as a wrapper to retrieve information about the defined parameter.

    public abstract class OptimizationParameter
    {
        protected Parameter parameter;

        public bool optimized = false;

        public string Name
        {
            get { return parameter.identifier; }
        }

        public double Value
        {
            get { return Double.Parse(parameter.value); }
        }

        public int Index
        {
            get { return (parameter.id - 1); }
        }

        public string SearchName;
        

        public OptimizationParameter(string paramName) 
        {
            SearchName = paramName;
        }

        internal bool FindandSetParameter(Parameter[] parametersToSearch)
        {
            try
            {
                parameter = parametersToSearch.FirstOrDefault(x => String.Compare(x.identifier, SearchName, true) == 0);
                return true;
            }
            catch
            {
                throw new Exception("Search Parameter not found in avaliable Project Parmeters");
            }
        }
    }

We can now define additional classes relating to particular optimization parameters. This defines specifics on how a certain parameter is to be updated as we loop through an optimisation procedure. We can also set here a reference to how the outputed result will affect the optimization.

Here a WeldThicknessOptiziationParameter class is created. In the constructor, we provide an index to the weldResultRef. This is the index of the weld result which directly relates to the weld thickness parameter we have defined.

Referencing weld results need to be achieved manually, and hopefully can be improved in the future.

Methods of setInitial() and Increment() are added to the WeldThicknessOptiziationParameter which serve to update the stored user-defined parameter.

    public class WeldThinknessOptimizationParameter : OptimizationParameter //inheret for the serialation
    {
        public int ConnectionResultWeldRef;
        
        public WeldThinknessOptimizationParameter(string paramName, int weldResultRef) : base(paramName)
        {
            ConnectionResultWeldRef = weldResultRef;
        }

        public override SimpleResult ExtractReferenceResult(ConnectionCheckRes connectionResult)
        {
            List<CheckResWeld> weldResults = connectionResult.CheckResWeld;

            try
            {
                CheckResWeld weldResult = weldResults[ConnectionResultWeldRef];
                return new SimpleResult(weldResult.CheckStatus, weldResult.UnityCheck);
            }
            catch
            {
                throw new Exception("Weld Result not Avaliable");
            }
        }

        public void SetInitial(double start)
        {
            parameter.value = start.ToString();
        }

        public void Increment(double increment)
        {
            parameter.value = (Value + increment).ToString();
        }
    }

Serialising Parameters

In the above OptimizationParameter class parameters are stored as deserialized objects. This allows us to complete changes to the retrieved parameters easily before Applying them. Below is the Parameter class used to deserialize project parameters.

Some parameter types will provide a second level JSON string as the value, This can also be deserialized as #########.

For more information on the serialization of user-defined parameters refer [here].

    public class Parameter
    {
        public int id { get; set; }
        public string identifier { get; set; }
        public string description { get; set; }
        public string parameterType { get; set; }
        public string value { get; set; }
    }

Running the Optimization

Now we have the basis for running our optimization we can pull it all together to set up a simple optimization procedure.

We can use some of the available features of the IDEA API to Save to the project at each step.

    class Program
    {
        static void Main(string[] args)
        {
	    string ideaStaticaInstallDir = args[0];

	    // create the instance of the factory 
	    ConnHiddenClientFactory calcFactory = new ConnHiddenClientFactory(ideaStaticaInstallDir);

	    //create the instance of the IDEA StatiCa Client
	    ConnectionHiddenCheckClient client = calcFactory.Create();

	    //Gets paths of resource in project.
	    string RunningPath = AppDomain.CurrentDomain.BaseDirectory;
	    string IdeaConFileName = string.Format("{0}Resources\\Tube-Tube Weld Optimisation.ideaCon", Path.GetFullPath(Path.Combine(RunningPath, @"..\..\")));

	    //path to executing assembly bin folder
	    Assembly assembly = Assembly.GetExecutingAssembly();
	    string baseConFileDirectory = Path.GetDirectoryName(assembly.Location);
			
	    try
	    {
		//Open project
		client.OpenProject(IdeaConFileName);

		try
		{
		    ConnectionInfo conInfo = client.GetProjectInfo().Connections.First();
		    string conIdentifier = conInfo.Identifier;

		    //Set the Optimization Parameters - No API interaction Required
		    List<WeldThinknessOptimizationParameter> weldParamsToOpt = new List<WeldThinknessOptimizationParameter>();
		    weldParamsToOpt.Add(new WeldThinknessOptimizationParameter("ColWeld", 0));
		    weldParamsToOpt.Add(new WeldThinknessOptimizationParameter("ChordWeld", 1));

		    //Parameter Store
		    Parameter[] projectParameters; 

		    //Get Project Parameters JSON String
		    string conParamsJson = client.GetParametersJSON(conInfo.Identifier);

		    //Deserialize Parameters
		    projectParameters = JsonConvert.DeserializeObject<Parameter[]>(conParamsJson);

		    //Search and Set Parameters to Optimization Params
		    foreach (var weldParam in weldParamsToOpt)
			weldParam.FindandSetParameter(projectParameters);

		    //Set Weld Thickness Optimization Values
		    double startValue = 0.003; //Start Weld Thickness
		    double incrementValue = 0.0005; //Increment Value - Values to Increment
		    double maxValue = 0.01; //Max Value of Weld Thickness

		    //Set Optimisation Options
		    bool saveIterations = true;
		    int maxIterations = 5;

		    //Perform Optimization
		    onsole.WriteLine(string.Format("Starting Optimization Procedure"));
					
		    for (int iteration = 0; iteration < maxIterations; iteration++)
		    {
			Console.WriteLine(string.Format("Optimization Iteration {0}", iteration + 1));

			//If first run, set initial values else increment. Stop if max weld size is hit.
			if (iteration == 0)
			    foreach (var parameter in weldParamsToOpt)
				parameter.SetInitial(startValue);
			else
			    foreach (var parameter in weldParamsToOpt)
				if (!parameter.optimized)
				    if (parameter.Value < maxValue)
					parameter.Increment(incrementValue);
				    else
					Console.WriteLine(string.Format("Max Value of Weld Hit. Optimization Stopped"));
									
		        Console.WriteLine("Trying Parameter Values:");
			foreach (var parameter in weldParamsToOpt)
				Console.WriteLine(parameter.Name + ": Is Optimized = " + parameter.optimized + ", Weld Thickness = " + parameter.Value);

			//serialize updated parameters
			string updatedParameters = JsonConvert.SerializeObject(projectParameters);

			//apply updated parameters to project
			client.ApplyParameters(conIdentifier, updatedParameters);

			//calculate the connection with new parameters
			ConnectionResultsData results = client.Calculate(conIdentifier);

			//Get first connection result from result Data
			ConnectionCheckRes ConCheckRes = results.ConnectionCheckRes[0];

			//Check parameters and set whether optimised 
			foreach (var parameter in weldParamsToOpt)
			{
			    //Get status of directly referenced weld result
			    bool status = parameter.ExtractReferenceResult(ConCheckRes);
			    parameter.optimized = status;

			    Console.WriteLine(parameter.Name + ": Is Optimised = " + parameter.optimized + ", Weld Thickness = " + parameter.Value);
			}

			//Choose to save each Iteration results
			if (saveIterations)
			{
			    string filepath = Path.Combine(baseConFileDirectory, string.Format("Tube-Tube-WeldOptimisation_{0}.ideaCon", iteration+1));
			    client.SaveAsProject(filepath);
			}

			//Exit Optimisation Conditions
			bool exitOptimization = false;

			foreach (var opParam in weldParamsToOpt)
			{
			    if (!opParam.optimized)
				break;
			    exitOptimization = true;
			}

			if (exitOptimization)
			{
			    //client.Save();
			    break;
			}
	            }
		}
		finally
		{
		    client.CloseProject();
		}
            }
	    finally
	    {
		if (client != null)
		    client.Close();
            }
	    Console.WriteLine("End");
	}
    }

Further Information

The ability to add parameters to a project and then modify them through an optimization loop provides an endless amount of ways to start looking at ways you can optimize connection designs. Here we have looked at a rather simple example here. Mainly because there is a direct relationship between the weld size and the design. We can push these optimization parameters further to understand how other components may interact with other changes in the connection. For example, if a section size is changed, how does that affect the bolt force? Or weld design? Or both.

We will continue to develop the Connection API to enable optimization procedures, such as the one demonstrated here. Please get in touch if you have any comments or would like to discuss further.