Here’s a fun thing I’ve built as a Proof of Concept: the Genetic OptimizationButton.
Recently I used our AppBuilder to setup a nice truss calculator. Very easily this resulted into supports / loads and beams, and performed checks.
Now here’s the catch: I’d like to minimize the amount of material used. So I’ll have to update the cross sections, and then check if all checks are still passing. And then update again. And again. And again.
This is generally where optimizations come in, and I’d use the OptimizationButton. However with this truss, I have 7 cross sections with a configurable area. I need a smarter optimization button.
Optimization button
Therefore I thought I’d lay the foundations for a more complex Optimization Button; the Genetic OptimizationButton.
By simply wrapping the optimization function with the genetic_optimize decorator, it turns the function into the function that will be ran many, many times in the algorithm. The genetic_optimize will automatically translate the inputs into genes, and will find the optimal values!
So by just these lines:
import viktor as vkt
from optimize_wrapper import genetic_optimize
class Parametrization(vkt.Parametrization):
x = vkt.NumberField("X")
y = vkt.NumberField("Y")
class Controller(vkt.Controller):
parametrization = Parametrization
@genetic_optimize(
optimized_parameters=Parametrization,
)
def optimize_x_and_y(self, optimized_params, params, **kwargs):
return optimized_params.x + optimized_params.y
I’d have a working application that will optimize x + y.
What does the wrapper do?
- It gets your Parametrization
- It gets the Fields from Parametrization, or from a specific Section/Tab/Page
- It creates a Gene field from the Fields
- It then runs the Controller function for everyone in the population. (The Controller function has optimized_params which change every run, and access to the normal kwargs)
- After finished, it will display the Results like the OptimizationButton with a nice image!
Result
So for my areas optimization this was my final function
Example areas optimization
@genetic_optimize(
optimized_parameters=Parametrization,
path="areas_section",
sol_per_pop=1000,
num_generations=500,
amount_of_solutions=50
)
def optimize_areas(self, optimized_params, params, **kwargs):
# Extract input data and perform analysis
nodes = list(params.geometry_section.nodes_table)
members = list(params.geometry_section.members_table)
loads = list(params.loads_section.loads_table)
supports = list(params.supports_section.supports_table)
# THESE ARE OPTIMIZED
areas_data = list(optimized_params.areas_section.areas_table)
# Proceed normally
E = params.material_section.youngs_modulus
stress_limit = params.analysis_section.stress_limit
num_nodes = len(nodes)
# Perform analysis
K_global = self._assemble_stiffness_matrix(nodes, members, areas_data, E)
F_global = np.zeros(2 * num_nodes)
for load in loads:
node_idx = load['node'] - 1
F_global[2*node_idx] = load['fx']
F_global[2*node_idx+1] = load['fy']
K_modified, F_modified, _ = self._apply_boundary_conditions(K_global, F_global, supports)
displacements = np.linalg.solve(K_modified, F_modified)
member_results = self._calculate_member_forces(nodes, members, areas_data, displacements, E)
# The fitness function is the total area of all crossections, with an insane penalty for if stress is over limit
for result in member_results:
stress = result['stress']
if abs(stress) > stress_limit:
return - sum([a.area for a in areas_data]) * 100
return - sum([a.area for
I think this is a very handy setup, which can be improved upon massively. But I thought I share it for inspiration!

