# -*- coding: utf-8 -*-
"""Command-line utility to run topology optimization."""
import textwrap
import argparse
import numpy
import topopt.guis
import topopt.boundary_conditions
import topopt.problems
import topopt.filters
import topopt.solvers
[docs]def create_parser(nelx: int = 180, nely: int = 60, volfrac: float = 0.4,
penalty: float = 3.0, rmin: float = 5.4, ft: int = 1
) -> argparse.ArgumentParser:
"""
Create an argument parser with the given values as defaults.
Parameters
----------
nelx:
The default number of elements in the x direction.
nely:
The default number of elements in the y direction.
volfrac:
The default fraction of the total volume to use.
penalty:
The default penalty exponent value in SIMP.
rmin:
The default filter radius.
ft:
The default filter method to use.
- ``0``: :obj:`topopt.filters.SensitivityBasedFilter`
- ``1``: :obj:`topopt.filters.DensityBasedFilter`
Returns
-------
Argument parser with given defaults.
"""
parser = argparse.ArgumentParser(description="Topology Optimization")
parser.add_argument("--nelx", type=int, default=nelx,
help="number of elements in the x direction")
parser.add_argument("--nely", type=int, default=nely,
help="number of elements in the x direction")
parser.add_argument("--volfrac", type=float, default=volfrac,
help="fraction of the total volume usable")
parser.add_argument("--penalty", type=float, default=penalty,
help="penalty exponent value in SIMP")
parser.add_argument("--rmin", "--filter-radius", type=float, dest="rmin",
default=rmin, help="filter radius")
parser.add_argument(
"--ft", "--filter-type", choices=[0, 1], dest="ft", default=ft,
help="filter type (0: sensitivity based, 1: density based)")
return parser
[docs]def parse_args(nelx: int = 180, nely: int = 60, volfrac: float = 0.4,
penalty: float = 3.0, rmin: float = 5.4, ft: int = 1
) -> tuple:
"""
Parse the system args with the given values as defaults.
Parameters
----------
nelx:
The default number of elements in the x direction.
nely:
The default number of elements in the y direction.
volfrac:
The default fraction of the total volume to use.
penalty:
The default penalty exponent value in SIMP.
rmin:
The default filter radius.
ft:
The default filter method to use.
- ``0``: :obj:`topopt.filters.SensitivityBasedFilter`
- ``1``: :obj:`topopt.filters.DensityBasedFilter`
Returns
-------
Parsed command-line arguments.
"""
args = create_parser(nelx, nely, volfrac, penalty, rmin, ft).parse_args()
return args.nelx, args.nely, args.volfrac, args.penalty, args.rmin, args.ft
[docs]def title_str(nelx: int, nely: int, volfrac: float, rmin: float,
penalty: float) -> str:
"""
Create a title string for the problem.
Parameters
----------
nelx:
The number of elements in the x direction.
nely:
The number of elements in the y direction.
volfrac:
The fraction of the total volume to use.
rmin:
The filter radius.
penalty:
The penalty exponent value in SIMP.
Returns
-------
Title string for the GUI.
"""
return textwrap.dedent(f"""\
dims: {nelx:d} x {nely:d}
volfrac: {volfrac:g}, penalty: {penalty:g}, rmin: {rmin:g}""")
[docs]def main(nelx: int, nely: int, volfrac: float, penalty: float, rmin: float,
ft: int, gui: topopt.guis.GUI = None,
bc: topopt.boundary_conditions.BoundaryConditions = None,
problem: topopt.problems.Problem = None,
filter: topopt.filters.Filter = None,
solver: topopt.solvers.TopOptSolver = None) -> None:
"""
Run the main application of the command-line tools.
Parameters
----------
nelx:
The number of elements in the x direction.
nely:
The number of elements in the y direction.
volfrac:
The fraction of the total volume to use.
penalty:
The penalty exponent value in SIMP.
rmin:
The filter radius.
ft:
The filter method to use.
- ``0``: :obj:`topopt.filters.SensitivityBasedFilter`
- ``1``: :obj:`topopt.filters.DensityBasedFilter`
gui:
The GUI to use.
bc:
The boundary conditions to use.
problem:
The problem to use.
filter:
The filter to use.
solver:
The solver to use.
"""
# Allocate design variables (as array), initialize and allocate sens.
x = volfrac * numpy.ones(nely * nelx, dtype=float)
# Initialize optional arguments
if solver is None:
if problem is None:
if bc is None:
bc = topopt.boundary_conditions.MBBBeamBoundaryConditions(
nelx, nely)
problem = topopt.problems.ComplianceProblem(bc, penalty)
gui = gui if gui else topopt.guis.GUI(
problem, title_str(nelx, nely, volfrac, rmin, penalty))
filter = (filter if filter else [
topopt.filters.SensitivityBasedFilter,
topopt.filters.DensityBasedFilter][ft](nelx, nely, rmin))
solver = topopt.solvers.TopOptSolver(problem, volfrac, filter, gui)
print(textwrap.dedent(f"""\
{solver:s}
dims: {nelx:d} x {nely:d}
volfrac: {volfrac:g}, penalty: {penalty:g}, rmin: {rmin:g}
Filter method: {solver.filter:s}"""))
# Solve the problem
x_opt = solver.optimize(x)
x_opt = solver.filter_variables(x_opt)
if gui is not None:
gui.update(x_opt)
# Make sure the plot stays and that the shell remains
input("Press any key...")