Creating A Project

This example shows how to create from scratch a DCS project consisting of an Ansys APDL beam model of a tubular steel trellis motorbike-frame. After creating the project configuration, 10 design points with randomly chosen parameter values are created and set to pending.

motorbike frame picture

The model is parametrized as follows:

  • three custom tube types are defined whose radius and thickness can vary in a certain range;

  • for each tube in the frame there is a string parameter specifying which custom type it should be made of;

  • output parameters of interest are the weight, the torsion stiffness and the maximum von Mises stress for a breaking load case.

For further details about the finite element model and its parametrization, see “Using Evolutionary Methods with a Heterogeneous Genotype Representation for Design Optimization of a Tubular Steel Trellis Motorbike-Frame”, 2003 by U. M. Fasel, O. Koenig, M. Wintermantel and P. Ermanni.

The project setup script as well as the data files can be downloaded here MAPDL Motorbike Frame Project.

We shall now dissect the project setup script in detail.

Import modules and instantiate the DPS client

The script starts by importing some generic modules and some key ansys-dcs-client classes. Next we connect to a DCS server running on the localhost with default username and password.

import os
import random

from ansys.dcs.client.dps import Client
from ansys.dcs.client.dps.resource import (Configuration, DesignPoint, File,
                                           FitnessDefinition, Project,
                                           SuccessCriteria)

client = Client(dcs_url="https://127.0.0.1/dcs", username="dcadmin", password="dcadmin")

Create an empty project and a configuration

Once the client is available, we can define a new project and send it to the server.

proj = Project(id="mapdl_motorbike_frame", display_name="MAPDL Motorbike Frame", priority=1, active=True)
proj = client.create_project(proj, replace=True)

Next, a configuration object is created.

cfg = Configuration(name="Configuration.1", active=True)

File resources

The first step to fill the project configuration is to define the files resources. Besides the name and type, for each file we need to specify the evaluation_path, i.e. the relative path under which the file instance for a design point evaluation will be stored. Moreover, if the file needs to be uploaded to the server, we also need to provide the path to the local file as src argument.

cwd = os.path.dirname(__file__)
files = []
# Input File
files.append (File( name="mac",evaluation_path="motorbike_frame.mac",
                    type="text/plain", src=os.path.join(cwd, "motorbike_frame.mac") ) )
# Output Files
files.append( File( name="results", evaluation_path="motorbike_frame_results.txt", type="text/plain" ) )
files.append( File( name="img", evaluation_path="file000.jpg", type="image/jpeg", collect=True) )
files.append( File( name="img2", evaluation_path="file001.jpg", type="image/jpeg", collect=True) )
files.append( File( name="out", evaluation_path="file.out", type="text/plain", collect=True) )

File resources are then created on the server.

files = proj.create_files(files)

# For convenience, we keep a reference to the input and result files.
mac_file = files[0]
result_file = files[1]

Parameters definition

Creating a parameter requires to first provide a parameter definition and then specify a parameter location.

# Input params: Dimensions of three custom tubes
float_input_params=[]
for i in range(1,4):
    pd = cfg.add_float_parameter_definition(name='tube%i_radius' %i, lower_limit=4.0, upper_limit=20.0,default=12.0 )
    cfg.add_parameter_location(key_string='radius(%i)' % i, tokenizer="=", parameter_definition_name=pd.name, file_id=mac_file.id)
    float_input_params.append(pd)
    pd = cfg.add_float_parameter_definition(name='tube%i_thickness' %i,lower_limit=0.5, upper_limit=2.5, default=1.0 )
    cfg.add_parameter_location(key_string='thickness(%i)' % i, tokenizer="=", parameter_definition_name=pd.name, file_id=mac_file.id)
    float_input_params.append(pd)

# Input params: Custom types used for all the different tubes of the frame
str_input_params=[]
for i in range(1,22):
    pd = cfg.add_string_parameter_definition(name="tube%s" %i, default="1", value_list=["1","2","3"] )
    cfg.add_parameter_location(key_string='tubes(%i)' % i, tokenizer="=", parameter_definition_name=pd.name, file_id=mac_file.id)
    str_input_params.append(pd)

# Output Parames
for pname in ["weight", "torsion_stiffness", "max_stress"]:
    pd = cfg.add_float_parameter_definition(name=pname)
    cfg.add_parameter_location(key_string=pname, tokenizer="=", parameter_definition_name=pd.name, file_id=result_file.id)

Process Step

In a process step, we specify which application should be executed, its requirements, which input and output files are linked to it, and optionally also the criteria for determining whether the process step completes successfully.

# Process step
cfg.add_process_step(   name="MAPDL_run",
                        application_name="ANSYS Mechanical APDL",
                        application_version="20.1",
                        execution_command="\ %\ executable\ %\ -b -i \ %\file:mac\ %\ -o file.out",
                        max_execution_time=20.0,
                        cpu_core_usage=1,
                        execution_level=0,
                        memory=250,
                        disk_space=5,
                        input_file_ids=[f.id for f in files[:1]],
                        output_file_ids=[f.id for f in files[1:]],
                        success_criteria= SuccessCriteria(
                            return_code=0,
                            expressions= ["values['tube1_radius']>=4.0", "values['tube1_thickness']>=0.5"],
                            required_output_file_ids=[ f.id for f in files[2:] ],
                            require_all_output_files=False,
                            required_output_parameter_names=["weight", "torsion_stiffness", "max_stress"],
                            require_all_output_parameters=False
                        ) )

Note that multiple process steps can be defined.

Fitness definition

In an optimization context, different type of fitness terms can be combined into a fitness definition object.

fd = FitnessDefinition(error_fitness=10.0)
fd.add_fitness_term(name="weight", type="design_objective", weighting_factor=1.0,
                    expression="map_design_objective( values['weight'], 7.5, 5.5)")
fd.add_fitness_term(name="torsional_stiffness", type="target_constraint", weighting_factor=1.0,
                expression="map_target_constraint( values['torsion_stiffness'], 1313.0, 5.0, 30.0 )" )
fd.add_fitness_term(name="max_stress", type="limit_constraint", weighting_factor=1.0,
                expression="map_limit_constraint( values['max_stress'], 451.0, 50.0 )")
cfg.fitness_definition =fd

Submit the configuration

The next step is to send the configuration to the server.

cfg = proj.create_configurations([cfg])[0]

Design Points

Finally, 10 design points with randomly chosen parameter values are created and set to pending.

dps = []
for i in range(10):
    values = { p.name : p.lower_limit + random.random()*(p.upper_limit-p.lower_limit)  for p in float_input_params }
    values.update({ p.name: random.choice(p.value_list) for p in str_input_params})
    dps.append( DesignPoint( name=f"DesignPoint.{i}", values=values, eval_status="pending") )

dps = cfg.create_design_points(dps)