Skip to content

Commit

Permalink
Fixed name shadowing issue and added more documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
cmccomb committed Jan 14, 2024
1 parent 9d0bbf7 commit 123ebbe
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 68 deletions.
10 changes: 6 additions & 4 deletions tests/test_optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,10 @@ def test_joint_optimization(self):
goals = trussme.Goals()

x0, obj, con, gen, bnds = trussme.make_optimization_functions(
truss_from_commands, goals, joint_coordinates=True, shape_parameters=False
truss_from_commands,
goals,
joint_optimization="full",
member_optimization=None,
)

results = scipy.optimize.minimize(
Expand Down Expand Up @@ -173,9 +176,8 @@ def test_full_optimization(self):
x0, obj, con, gen, bnds = trussme.make_optimization_functions(
truss_from_commands,
goals,
joint_coordinates=True,
shape_parameters=True,
shape_parameter_treatment="full",
joint_optimization="full",
member_optimization="full",
)

results = scipy.optimize.minimize(
Expand Down
150 changes: 86 additions & 64 deletions trussme/optimize.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@

def make_x0(
truss: Truss,
joint_coordinates: bool = True,
shape_parameters: bool = True,
shape_parameter_treatment: Literal["scaled", "full"] = "scaled",
joint_optimization: Literal[None, "full"] = "full",
member_optimization: Literal[None, "scaled", "full"] = "full",
) -> list[float]:
"""
Returns a vector that encodes the current truss design
Expand All @@ -18,10 +17,12 @@ def make_x0(
----------
truss: Truss
The truss to configure.
joint_coordinates: bool, default=True
Whether to include joint location parameters.
shape_parameters: bool, default=True
Whether to include shape parameters.
joint_optimization: Literal[None, "full"], default = "full"
If None, no optimization of joint location. If "full", then full optimization of joint locations will be used.
member_optimization: Literal[None, "scaled", "full"], default = "full"
If None, no optimization of member cross-section is performed. If "scaled", then member cross-section is
optimally scaled based on initial shape. If "full", then member cross-section parameters will be separately
optimized.
Returns
-------
Expand All @@ -34,7 +35,7 @@ def make_x0(

configured_truss = read_json(truss.to_json())

if joint_coordinates:
if joint_optimization:
for i in range(len(configured_truss.joints)):
if (
numpy.sum(configured_truss.joints[i].translation_restricted)
Expand All @@ -48,7 +49,7 @@ def make_x0(
if planar_direction != "z":
x0.append(configured_truss.joints[i].coordinates[2])

if shape_parameters and shape_parameter_treatment == "scaled":
if member_optimization == "scaled":
for i in range(len(configured_truss.members)):
shape_name: str = configured_truss.members[i].shape.name()
if shape_name == "pipe":
Expand All @@ -60,7 +61,7 @@ def make_x0(
elif shape_name == "square":
x0.append(configured_truss.members[i].shape._params["w"])

if shape_parameters and shape_parameter_treatment == "full":
if member_optimization == "full":
for i in range(len(configured_truss.members)):
shape_name: str = configured_truss.members[i].shape.name()
if shape_name == "pipe":
Expand All @@ -81,9 +82,8 @@ def make_x0(

def make_bounds(
truss: Truss,
joint_coordinates: bool = True,
shape_parameters: bool = True,
shape_parameter_treatment: Literal["scaled", "full"] = "scaled",
joint_optimization: Literal[None, "full"] = "full",
member_optimization: Literal[None, "scaled", "full"] = "full",
) -> tuple[list[float], list[float]]:
"""
Returns a vector that encodes the current truss design
Expand All @@ -92,10 +92,12 @@ def make_bounds(
----------
truss: Truss
The truss to configure.
joint_coordinates: bool, default=True
Whether to include joint location parameters.
shape_parameters: bool, default=True
Whether to include shape parameters.
joint_optimization: Literal[None, "full"], default = "full"
If None, no optimization of joint location. If "full", then full optimization of joint locations will be used.
member_optimization: Literal[None, "scaled", "full"], default = "full"
If None, no optimization of member cross-section is performed. If "scaled", then member cross-section is
optimally scaled based on initial shape. If "full", then member cross-section parameters will be separately
optimized.
Returns
-------
Expand All @@ -109,7 +111,7 @@ def make_bounds(

configured_truss = read_json(truss.to_json())

if joint_coordinates:
if joint_optimization:
for i in range(len(configured_truss.joints)):
if (
numpy.sum(configured_truss.joints[i].translation_restricted)
Expand All @@ -126,7 +128,7 @@ def make_bounds(
lb.append(-numpy.inf)
ub.append(numpy.inf)

if shape_parameters and shape_parameter_treatment == "scaled":
if member_optimization == "scaled":
for i in range(len(configured_truss.members)):
shape_name: str = configured_truss.members[i].shape.name()
if shape_name == "pipe":
Expand All @@ -142,7 +144,7 @@ def make_bounds(
lb.append(0.0)
ub.append(numpy.inf)

if shape_parameters and shape_parameter_treatment == "full":
if member_optimization == "full":
for i in range(len(configured_truss.members)):
shape_name: str = configured_truss.members[i].shape.name()
if shape_name == "pipe":
Expand All @@ -166,9 +168,8 @@ def make_bounds(

def make_truss_generator_function(
truss: Truss,
joint_coordinates: bool = True,
shape_parameters: bool = True,
shape_parameter_treatment: Literal["scaled", "full"] = "scaled",
joint_optimization: Literal[None, "full"] = "full",
member_optimization: Literal[None, "scaled", "full"] = "full",
) -> Callable[[list[float]], Truss]:
"""
Returns a function that takes a list of floats and returns a truss.
Expand All @@ -177,10 +178,12 @@ def make_truss_generator_function(
----------
truss: Truss
The truss to configure.
joint_coordinates: bool, default=True
Whether to include joint location parameters.
shape_parameters: bool, default=True
Whether to include shape parameters.
joint_optimization: Literal[None, "full"], default = "full"
If None, no optimization of joint location. If "full", then full optimization of joint locations will be used.
member_optimization: Literal[None, "scaled", "full"], default = "full"
If None, no optimization of member cross-section is performed. If "scaled", then member cross-section is
optimally scaled based on initial shape. If "full", then member cross-section parameters will be separately
optimized.
Returns
-------
Expand All @@ -194,7 +197,7 @@ def truss_generator(x: list[float]) -> Truss:
configured_truss = read_json(truss.to_json())
idx = 0

if joint_coordinates:
if joint_optimization:
for i in range(len(configured_truss.joints)):
if (
numpy.sum(configured_truss.joints[i].translation_restricted)
Expand All @@ -211,7 +214,7 @@ def truss_generator(x: list[float]) -> Truss:
configured_truss.joints[i].coordinates[2] = x[idx]
idx += 1

if shape_parameters and shape_parameter_treatment == "scaled":
if member_optimization == "scaled":
for i in range(len(configured_truss.members)):
shape_name: str = configured_truss.members[i].shape.name()
p = configured_truss.members[i].shape._params
Expand All @@ -234,7 +237,7 @@ def truss_generator(x: list[float]) -> Truss:
)
idx += 1

if shape_parameters and shape_parameter_treatment == "full":
if member_optimization == "full":
for i in range(len(configured_truss.members)):
shape_name: str = configured_truss.members[i].shape.name()
if shape_name == "pipe":
Expand All @@ -260,38 +263,58 @@ def truss_generator(x: list[float]) -> Truss:
def make_inequality_constraints(
truss: Truss,
goals: Goals,
joint_coordinates: bool = True,
shape_parameters: bool = True,
shape_parameter_treatment: Literal["scaled", "full"] = "scaled",
joint_optimization: Literal[None, "full"] = "full",
member_optimization: Literal[None, "scaled", "full"] = "full",
) -> Callable[[list[float]], list[float]]:
"""
Returns a function that evaluates the inequality constraints.
Parameters
----------
truss: Truss
The truss to configure.
goals: Goals
This informs constraints on yielding FOS, buckling FOS, and deflection.
joint_optimization: Literal[None, "full"], default = "full"
If None, no optimization of joint location. If "full", then full optimization of joint locations will be used.
member_optimization: Literal[None, "scaled", "full"], default = "full"
If None, no optimization of member cross-section is performed. If "scaled", then member cross-section is
optimally scaled based on initial shape. If "full", then member cross-section parameters will be separately
optimized.
Returns
-------
Callable[[list[float]], list[float]]
A function that evaluates constraints for the truss
"""
truss_generator = make_truss_generator_function(
truss, joint_coordinates, shape_parameters, shape_parameter_treatment
truss, joint_optimization, member_optimization
)

def inequality_constraints(x: list[float]) -> list[float]:
truss = truss_generator(x)
truss.analyze()
recon_truss = truss_generator(x)
recon_truss.analyze()
constraints = [
goals.minimum_fos_buckling - truss.fos_buckling,
goals.minimum_fos_yielding - truss.fos_yielding,
truss.deflection - numpy.min([goals.maximum_deflection, 10000.0]),
goals.minimum_fos_buckling - recon_truss.fos_buckling,
goals.minimum_fos_yielding - recon_truss.fos_yielding,
recon_truss.deflection - numpy.min([goals.maximum_deflection, 10000.0]),
]
if shape_parameters and shape_parameter_treatment == "full":
for i in range(len(truss.members)):
shape_name: str = truss.members[i].shape.name()
if member_optimization == "full":
for i in range(len(recon_truss.members)):
shape_name: str = recon_truss.members[i].shape.name()
if shape_name == "pipe":
constraints.append(
truss.members[i].shape._params["t"]
- truss.members[i].shape._params["r"]
recon_truss.members[i].shape._params["t"]
- recon_truss.members[i].shape._params["r"]
)
elif shape_name == "box":
constraints.append(
truss.members[i].shape._params["t"]
- truss.members[i].shape._params["w"]
recon_truss.members[i].shape._params["t"]
- recon_truss.members[i].shape._params["w"]
)
constraints.append(
truss.members[i].shape._params["t"]
- truss.members[i].shape._params["h"]
recon_truss.members[i].shape._params["t"]
- recon_truss.members[i].shape._params["h"]
)

return constraints
Expand All @@ -302,9 +325,8 @@ def inequality_constraints(x: list[float]) -> list[float]:
def make_optimization_functions(
truss: Truss,
goals: Goals,
joint_coordinates: bool = True,
shape_parameters: bool = True,
shape_parameter_treatment: Literal["scaled", "full"] = "scaled",
joint_optimization: Literal[None, "full"] = "full",
member_optimization: Literal[None, "scaled", "full"] = "full",
) -> tuple[
list[float],
Callable[[list[float]], float],
Expand All @@ -313,17 +335,18 @@ def make_optimization_functions(
tuple[list[float], list[float]],
]:
"""
Creates functions for use in optimization, including a starting vector, objective function, a constraint function, and a truss generator function.
Creates functions for use in optimization, including a starting vector, objective function, a constraint function,
and a truss generator function.
Parameters
----------
truss: Truss
The truss to use as a starting configuration
goals: Goals
The goals to use for optimization
joint_coordinates: bool, default=True
Whether to include joint location parameters.
shape_parameters: bool, default=True
joint_optimization: Literal[None, "full"] = "full"
If None, no optimization of joint location. If "full", then full optimization of joint locations will be used.
member_optimization: Literal[None, "scaled", "full"] = "full",
Whether to include shape parameters.
Returns
Expand All @@ -333,33 +356,32 @@ def make_optimization_functions(
Callable[[list[float]], float],
Callable[[list[float]], list[float]],
Callable[[list[float]], Truss],
tuple[list[float], list[float]]
]
A tuple containing the starting vector, objective function, constraint function, and truss generator function.
:param goals:
"""

x0 = make_x0(truss, joint_coordinates, shape_parameters, shape_parameter_treatment)
x0 = make_x0(truss, joint_optimization, member_optimization)

truss_generator = make_truss_generator_function(
truss, joint_coordinates, shape_parameters, shape_parameter_treatment
truss, joint_optimization, member_optimization
)

bounds = make_bounds(
truss, joint_coordinates, shape_parameters, shape_parameter_treatment
lower_bounds, upper_bounds = make_bounds(
truss, joint_optimization, member_optimization
)

inequality_constraints = make_inequality_constraints(
truss, goals, joint_coordinates, shape_parameters, shape_parameter_treatment
truss, goals, joint_optimization, member_optimization
)

def objective_function(x: list[float]) -> float:
truss = truss_generator(x)
return truss.mass
return truss_generator(x).mass

return (
x0,
objective_function,
inequality_constraints,
truss_generator,
bounds,
(lower_bounds, upper_bounds),
)

0 comments on commit 123ebbe

Please sign in to comment.