Create your own optimization criterion#
This section aims to address concerns for a developer and does therefore require a deeper understanding of the underlying implementation details. The aim of this section is to explain how to implement your own optimization criterion. We strongly recommend to read the documentation related to the criteria conjointly.
Principles#
All the criteria are available in the module OpenPisco.Optim.Criteria. For practical purposes, there are two main types of criteria considered:
Geometrical: inherit from base class
OpenPisco.Optim.Criteria.Criteria.CriteriaBasePhysical: inherit from base class
OpenPisco.Optim.Criteria.Criteria.PhysicalCriteriaBase, which also inherit fromOpenPisco.Optim.Criteria.Criteria.CriteriaBase
The main difference between them is the endowed physical problem(s) OpenPisco.Optim.Criteria.Criteria.PhysicalCriteriaBase.problem within a physical criterion, which must be run in order to compute the value of the criteria. Typically, its value arises from a postprocessing of the underlying solution to a PDE systems, meaning that such an attribute is an instance of a physical solver supported by the platform.
For the sake of completeness, note that a geometrical criterion could also involve the resolution of a PDE problem, as long as it is related to the actual shape of the structure to be optimized.
In order to illustrate that, here is an implementation example for a typical physical criterion
from OpenPisco.Optim.Criteria.CriteriaFactory import RegisterClass as RegisterCriteriaClass
class MyAwesomeCriteria(PhysicalCriteriaBase):
def __init__(self, other=None):
super(MyAwesomeCriteria).__init__(other)
if other is None:
self.SetName("MyAwesomeTopoCriteria")
self.instanceParameter = 0
else:
self.instanceParameter = other.instanceParameter
def UpdateValues(self,levelSet):
#Compute criteria value
self.f_val=criteriaValue
#Compute criteria sensitivity
self.fSensitivity_val=sensitivityValue
return RETURN_SUCCESS
def GetNumberOfSolutions(self):
#Provide numberOfSolution
return numberOfSolution
def GetSolution(self,i):
if i >= self.GetNumberOfSolutions():
raise(Exception("i out of bounds "))
#Retrieve solution i
return ithSolution
def GetSolutionName(self,i):
if i >= self.GetNumberOfSolutions():
raise(Exception("i out of bounds "))
#Retrieve ith solution name
return ithSolutionName
Before proceeding, the concept of solutions within a criterion should be explained. A given criterion can involve several fields of interest, including the underlying solutions to the PDE system hidden in the OpenPisco.Optim.Criteria.Criteria.PhysicalCriteriaBase.problem which should be the first field of interest to consider in the general case.
Throught the optimization process, it can be interesting to export such fields in order to observe how the evolution on the shape impacts them; this is why we provide this possibility for each criteria.
Reading the other mecanism present in the constructor, its purpose is to enable the copy of an instance-defined constant parameter throught the topology optimization process.
The constraints and responsibilities for the developer are as follows:
Redefinition of the behavior of
OpenPisco.Optim.Criteria.Criteria.CriteriaBase.UpdateValues(): it is the main method. This method is the one actually called by the optimization problem (OpenPisco.Optim.Problems.OptimProblemConcrete.UpdateValues()) to update the criteria values/sensitivities. Note that it must return the criterion state at the end of the computation, here RETURN_SUCCESS, if it suceeds.Redefine the behavior of
OpenPisco.Optim.Criteria.Criteria.CriteriaBase.GetNumberOfSolutions(): return the number of field of interest you would like to exportRedefine the behavior of
OpenPisco.Optim.Criteria.Criteria.CriteriaBase.GetSolution(): return a field of interest (array) you would like to export for a given indexRedefine the behavior of
OpenPisco.Optim.Criteria.Criteria.CriteriaBase.GetSolutionName(): return the name of a field of interest (array) you would like to export for a given index
In order to add a new optimization criterion, a developer should redefine the behavior of few methods.
Method |
Notes |
|---|---|
|
Update the criteria value/sensitivities |
|
return the number of field (int) of interest to export |
|
return a field of interest (array) to export |
|
return the name (str) of a field of interest (array) to export |
Note
The number of solutions provided should be consistent between these three methods (same number of names, same number of fields and correct number of solution provided).
Compatibility with OpenPisco and OpenPiscoCL applications#
Let us consider an example from the tutorial Topology optimization of a cantilever beam using unstructured conformal level sets with the compliance criterion.
<OptimProblems>
<OptimProblem type="TopoGeneric" id="1"
OnZone="8"
useLevelset="1"
printMeshQualityInfo = "True"
output="3"
outputEveryLevelsetUpdate = "False">
<Objective type="Volume" name="Volume"/>
<Constraint type="Compliance" name="Compliance" UpperBound="2.5*4.69434000e-01" useProblem="1" />
</OptimProblem>
</OptimProblems>
In order to enable the use of a criterion in the application, in the OpenPisco.Optim.Criteria.PhyCriteria module, we add the mapping between the criterion name and the associated class to the criteria factory.
from OpenPisco.Optim.Criteria.CriteriaFactory import RegisterClass as RegisterCriteriaClass
RegisterCriteriaClass("Compliance",TopoCriteriaCompliance)
where:
“Compliance” is the name used in the input block of code above, for the applications
TopoCriteriaCompliance is the class
OpenPisco.Optim.Criteria.PhyMecaCriteria.TopoCriteriaComplianceassociated to the compliance criteria
Note only the simplest configuration is covered above; it is implicitly assumed that the name of the criterion is enough to build the associated criterion instance. Let us consider another criterion in order to see how to handle more complex cases
<OptimProblems>
<OptimProblem type="TopoGeneric" id="1"
OnZone="8"
useLevelset="1"
printMeshQualityInfo = "True"
output="3"
outputEveryLevelsetUpdate = "False">
<Objective type="Volume" name="Volume"/>
<Constraint type="NodalTargetDisp" name="NodalTargetDisp" TargetValue="1.e-3" useProblem="1" dir="0 0 1" u0="0.5" nTag="NT3"/>
</OptimProblem>
</OptimProblems>
Just like the physical solver, such a block of code becomes internally a mere dictionary. The values within it have to be passed to the criterion instance for its proper initialization. In order to do that, unlike the previous case, we need the associated constructor-like function. For instance
from OpenPisco.Optim.Criteria.CriteriaFactory import RegisterClass as RegisterCriteriaClass
RegisterCriteriaClass("NodalTargetDisp", TopoCriteriaNodalTargetDisp,CreateTopoCriteriaNodalTargetDisp)
where:
“NodalTargetDisp” is the name used in the input block of code above, for the applications
TopoCriteriaNodalTargetDisp is the class
OpenPisco.Optim.Criteria.PhyMecaCriteria.TopoCriteriaNodalTargetDispassociated to the nodal target displacement criteriaCreateTopoCriteriaNodalTargetDisp, mentioned above, is the general constructor-like function for the criterion considered. Note that its implementation (
OpenPisco.Optim.Criteria.PhyMecaCriteria.CreateTopoCriteriaNodalTargetDisp()) depends on the underlying solver interface implementation.
Finally, make sure your new file is imported in OpenPisco/Optim/Criteria/CriteriaFactory.py within the InitAllCriteria() function, so the factory is aware of it at startup.