Skip to content

Commit

Permalink
Merge pull request #22 from cserteGT3/tests
Browse files Browse the repository at this point in the history
Add tests
  • Loading branch information
cserteGT3 authored Sep 14, 2023
2 parents 77279fc + fbe2d0e commit c5c526f
Show file tree
Hide file tree
Showing 8 changed files with 289 additions and 40 deletions.
6 changes: 4 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ DataFrames = "1"
JuMP = "1.4"
Meshes = "0.34,0.35"
PrettyTables = "2"
julia = "1.9"
Rotations = "1.5"
julia = "1.9"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9"
Meshes = "eacbb407-ea5a-433e-ab97-5258b1ca43fa"

[targets]
test = ["Test"]
test = ["Test", "Ipopt", "Meshes"]
68 changes: 35 additions & 33 deletions docs/src/example.md

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions src/geometries.jl
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,12 @@ struct Tolerance
note::String
end

"""
MultiOperationProblem
Collect all data for a multi operation problem, including: part zeros, holes, planes,
tolerances, parameters and optimization result.
"""
mutable struct MultiOperationProblem
partzeros::Vector{PartZero}
holes::Vector{HoleLocalizationFeature}
Expand All @@ -336,6 +342,23 @@ mutable struct MultiOperationProblem
opresult::OptimizationResult
end

"""
MultiOperationProblem(partzeros, holes, planes, tolerances, parameters)
Construct a multi operation problem.
For usage, please see the example section in the documentation.
The parameters for the optimization are also described there with greater details.
# Arguments
- `partzeros::Vector{PartZero}`: array of part zeros.
- `holes::Vector{HoleLocalizationFeature}`: array of holes.
- `planes::Vector{PlaneLocalizationFeature}`: array of planes.
- `tolerances::Vector{Tolerance}`: array of tolerances.
- `parameters::Dict{String,Any}`: parameters in the form of a dictionary. Keys include:
`minAllowance`, `OptimizeForToleranceCenter`, `UseTolerances`,
`SetPartZeroPosition`, `maxPlaneZAllowance`.
"""
function MultiOperationProblem(partzeros, holes, planes, tolerances, parameters)
return MultiOperationProblem(partzeros, holes, planes, tolerances, parameters, emptyor())
end
Expand Down
15 changes: 13 additions & 2 deletions src/optimization.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ end
function addplane2model!(::IsPrimitive, model, plane, ipzmatricedict)
# access registered variables
minAllowance = model[:minAllowance]
maxPlaneZAllowance = model[:maxPlaneZAllowance]

# register distance variable:
dz = @variable(model, base_name = string("d_z_", getfeaturename(plane)))
Expand All @@ -69,12 +70,14 @@ function addplane2model!(::IsPrimitive, model, plane, ipzmatricedict)
@constraint(model, dz == d_f[3])
# equation (7)
@constraint(model, -1*dz >= minAllowance)
@constraint(model, -1*dz <= maxPlaneZAllowance)
return model
end

function addplane2model!(::IsFreeForm, model, plane, ipzmatricedict)
# access registered variables
minAllowance = model[:minAllowance]
maxPlaneZAllowance = model[:maxPlaneZAllowance]

# filtered surface points of a free form surface
qs = getroughfilteredpoints(plane)
Expand All @@ -92,6 +95,7 @@ function addplane2model!(::IsFreeForm, model, plane, ipzmatricedict)
@constraint(model, dz[i] == d_f[3])
# equation (6)
@constraint(model, -1*dz[i] >= minAllowance)
@constraint(model, -1*dz[i] <= maxPlaneZAllowance)
end
return model
end
Expand Down Expand Up @@ -163,6 +167,8 @@ function createjumpmodel(mop::MultiOperationProblem, optimizer; disable_string_n
# variable for minimum allowance
#@variable(model, minAllowance >= 0)
@variable(model, minAllowance)
# variable for maximum allowance for planes
@variable(model, maxPlaneZAllowance)

## tolerances
addtolerances2model!(model, mop, pzmatricedict)
Expand All @@ -175,6 +181,10 @@ function createjumpmodel(mop::MultiOperationProblem, optimizer; disable_string_n
for p in machinedplanes
addplane2model!(model, p, ipzmatricedict)
end
# if maximum allowance of planes is given, then set it
if haskey(mop.parameters, "maxPlaneZAllowance")
@constraint(model, maxPlaneZAllowance == mop.parameters["maxPlaneZAllowance"])
end

# optimization
if mop.parameters["OptimizeForToleranceCenter"]
Expand Down Expand Up @@ -210,7 +220,8 @@ end

function setjumpresult!(mop::MultiOperationProblem, jump_model)
status = termination_status(jump_model)
if status != TerminationStatusCode(1)
if (status != OPTIMAL) & (status != LOCALLY_SOLVED)
mop.opresult = OptimizationResult(string(status), NaN)
@warn "Optimization did not find optimum! Ignoring result. Status: $status"
return mop
end
Expand All @@ -220,7 +231,7 @@ function setjumpresult!(mop::MultiOperationProblem, jump_model)
pz.position[j] = jump_result[i, j]
end
end
jump_status = string(termination_status(jump_model))
jump_status = string(status)
jump_minallowance = value(jump_model[:minAllowance])
or = OptimizationResult(jump_status, jump_minallowance)
mop.opresult = or
Expand Down
47 changes: 47 additions & 0 deletions test/geometries.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
@testset "IsPrimitive geometries" begin
sh = SimpleHole([0, 0, 0], 29)
sp = SimplePlane([0, 0, 0])

@test featurepoint(sh) == [0, 0, 0]
@test featureradius(sh) == 29
@test featurepoint(sp) == [0, 0, 0]

@test_throws MethodError featureradius(sp)
@test_throws ErrorException surfacepoints(sp)
@test_throws ErrorException surfacepoints(sh)
@test_throws ErrorException filteredsurfacepoints(sp)
@test_throws ErrorException filteredsurfacepoints(sh)

pz1 = PartZero("pz1", [0, 0, 0], hcat([0, 1, 0], [0, 0, 1], [1, 0, 0]))
sh_r = SimpleHole([82.5, 30, 40], 26)
sp_r = PlaneAndNormal([82.5, 30, 40], [1, 0, 0])
fd_sh = FeatureDescriptor("simple-hole", pz1, true, true)
fd_sp = FeatureDescriptor("simple-plane", pz1, true, true)

h1 = HoleLocalizationFeature(fd_sh, sh_r, sh)
p1 = PlaneLocalizationFeature(fd_sp, sp_r, sp)

@test BLC.getfeaturename(h1) == "simple-hole"
@test BLC.getfeaturename(p1) == "simple-plane"
@test BLC.getpartzero(h1) === BLC.getpartzero(p1) === pz1
@test BLC.getpartzeroname(h1) == "pz1"
@test BLC.hasrough(h1)
@test BLC.hasrough(p1)
@test BLC.hasmachined(h1)
@test BLC.hasmachined(p1)

@test BLC.getroughfeaturepoint(h1) == [82.5, 30, 40]
@test BLC.getroughfeaturepoint(p1) == [82.5, 30, 40]
@test BLC.getmachinedfeaturepoint(h1) == [0, 0, 0]
@test BLC.getmachinedfeaturepoint(p1) == [0, 0, 0]
@test BLC.getmachinedradius(h1) == 29
@test_throws MethodError BLC.getmachinedradius(p1)
@test BLC.getroughradius(h1) == 26
@test_throws MethodError BLC.getroughradius(p1)

@test_throws ErrorException BLC.getroughfilteredpoints(h1)
@test_throws ErrorException BLC.getroughfilteredpoints(p1)

@test BLC.getmachinedfeaturepointindatum(h1) == [0, 0, 0]
@test BLC.getmachinedfeaturepointindatum(p1) == [0, 0, 0]
end
18 changes: 18 additions & 0 deletions test/partzeros.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@testset "PartZero" begin
pz1 = PartZero("pz1", [0,0,0], hcat([0,1,0], [0,0,1], [1,0,0]))
pz2 = PartZero("pz", [0,0,0], hcat([0,1,0], [0,0,1], [1,0,0]))

@test BLC.xaxis(pz1) == [0, 1, 0]
@test BLC.yaxis(pz1) == [0, 0, 1]
@test BLC.zaxis(pz1) == [1, 0, 0]

M = BLC.getpartzeroHM(pz1)
Mt = [0 0 1 0; 1 0 0 0; 0 1 0 0; 0 0 0 1]
@test M == Mt

@test inv(Mt) == BLC.getpartzeroinverseHM(pz1)
@test inv(Mt) == BLC.inverthomtr(Mt)

pz = BLC.getpartzerobyname([pz1, pz2], "pz")
@test pz === pz2
end
11 changes: 8 additions & 3 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
using BlankLocalizationCore
using Test

@testset "BlankLocalizationCore.jl" begin
# Write your tests here.
end
using Meshes
using Ipopt

const BLC = BlankLocalizationCore

include("partzeros.jl")
include("geometries.jl")
include("testproblem.jl")
141 changes: 141 additions & 0 deletions test/testproblem.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
@testset "simple mop" begin
# this is the problem from the documentation:
# https://csertegt3.github.io/BlankLocalizationCore.jl/stable/example/
## Part zero definitions

pzf = PartZero("front", [0,0,0], hcat([0,1,0], [0,0,1], [1,0,0]))
pzr = PartZero("right", [0,0,0], hcat([-1, 0, 0], [0, 0, 1], [0, 1, 0]))
pzb = PartZero("back", [0,0,0], hcat([0, -1, 0], [0, 0, 1], [-1, 0, 0]))

partzeros = [pzf, pzr, pzb]

## Machined geometry definitions

fronthole_m = SimpleHole([0, 0, 0], 29)
frontface_m = SimplePlane([0, 0, 0])

righthole1_m = SimpleHole([16, 15, 0], 7.5)
righthole2_m = SimpleHole([25, -16, 3], 9)
righthole3_m = SimpleHole([60, 0, -3], 13.5)
rightface1_m = SimplePlane([16, 15, 0])
rightface2_m = SimplePlane([25, -16, 3])
rightface3_m = SimplePlane([60, 0, -3])

backhole1_m = SimpleHole([-14, 14, 0], 9)
backhole2_m = SimpleHole([14, 14, 0], 9)
backface1_m = SimplePlane([-14, 14, 0])
backface2_m = SimplePlane([14, 14, 0])

## Rough geometry definitions

fronthole_r = SimpleHole([82.5, 30, 40], 26)
frontface_r = PlaneAndNormal([82.5, 30, 40], [1, 0, 0])

righthole1_r = SimpleHole([66, 71.5, 55], 6)
righthole2_r = SimpleHole([58, 74.5, 24], 4.905)
righthole3_r = SimpleHole([21.5, 68.5, 40], 8)
rightface1_r = PlaneAndNormal([66, 71.5, 55], [0, 1, 0])
rightface2_r = PlaneAndNormal([58, 74.5, 24], [0, 1, 0])
rightface3_r = PlaneAndNormal([21.5, 68.5, 40], [0, 1, 0])

backhole1_r = SimpleHole([-3, 44, 53.9], 6.2)
backhole2_r = SimpleHole([-3, 16.1, 54], 6.25)
backface1_r = PlaneAndNormal([-3, 44, 54], [-1, 0, 0])
backface2_r = PlaneAndNormal([-3, 16, 54], [-1, 0, 0])

## Geometry pairing and feature descriptors

# Feature descriptors for each feature

fd_fronthole = FeatureDescriptor("fronthole", pzf, true, true)
fd_frontface = FeatureDescriptor("frontface", pzf, true, true)

fd_righthole1 = FeatureDescriptor("righthole1", pzr, true, true)
fd_righthole2 = FeatureDescriptor("righthole2", pzr, true, true)
fd_righthole3 = FeatureDescriptor("righthole3", pzr, true, true)
fd_rightface1 = FeatureDescriptor("rightface1", pzr, true, true)
fd_rightface2 = FeatureDescriptor("rightface2", pzr, true, true)
fd_rightface3 = FeatureDescriptor("rightface3", pzr, true, true)

fd_backhole1 = FeatureDescriptor("backhole1", pzb, true, true)
fd_backhole2 = FeatureDescriptor("backhole2", pzb, true, true)
fd_backface1 = FeatureDescriptor("backface1", pzb, true, true)
fd_backface2 = FeatureDescriptor("backface2", pzb, true, true)

# Hole features

holes = [HoleLocalizationFeature(fd_fronthole, fronthole_r, fronthole_m),
HoleLocalizationFeature(fd_righthole1, righthole1_r, righthole1_m),
HoleLocalizationFeature(fd_righthole2, righthole2_r, righthole2_m),
HoleLocalizationFeature(fd_righthole3, righthole3_r, righthole3_m),
HoleLocalizationFeature(fd_backhole1, backhole1_r, backhole1_m),
HoleLocalizationFeature(fd_backhole2, backhole2_r, backhole2_m)
]

# Face features
planes = [PlaneLocalizationFeature(fd_frontface, frontface_r, frontface_m),
PlaneLocalizationFeature(fd_rightface1, rightface1_r, rightface1_m),
PlaneLocalizationFeature(fd_rightface2, rightface2_r, rightface2_m),
PlaneLocalizationFeature(fd_rightface3, rightface3_r, rightface3_m),
PlaneLocalizationFeature(fd_backface1, backface1_r, backface1_m),
PlaneLocalizationFeature(fd_backface2, backface2_r, backface2_m)
]

## Tolerances

xfunc(x) = x[1]
yfunc(x) = x[2]
zfunc(x) = x[3]

tolerances = [Tolerance("rightface1", true, yfunc, "fronthole", true, 41, 40.7, 41.3, "1"),
Tolerance("backhole1", true, yfunc, "fronthole", true, 14, 13.8, 14.2, "2"),
Tolerance("fronthole", true, yfunc, "backhole2", true, 14, 13.8, 14.2, "3"),
Tolerance("backhole1", true, zfunc, "fronthole", true, 14, 13.8, 14.2, "4"),
Tolerance("backhole2", true, zfunc, "fronthole", true, 14, 13.8, 14.2, "5"),
Tolerance("rightface3", true, yfunc, "fronthole", true, 38, 37.7, 38.3, "6"),
Tolerance("rightface2", true, yfunc, "fronthole", true, 44, 43.7, 44.3, "7"),
Tolerance("frontface", true, xfunc, "righthole3", true, 60, 59.7, 60.3, "8"),
Tolerance("frontface", true, xfunc, "righthole2", true, 25, 24.8, 25.2, "9"),
Tolerance("frontface", true, xfunc, "righthole1", true, 16, 15.8, 16.2, "10"),
Tolerance("righthole1", true, zfunc, "fronthole", true, 15, 14.8, 15.2, "11"),
Tolerance("fronthole", true, zfunc, "righthole2", true, 16, 15.8, 16.2, "12"),
Tolerance("frontface", true, xfunc, "backface1", false, 85, 84.6, 85.4, "13"),
Tolerance("frontface", true, xfunc, "backface2", false, 85, 84.6, 85.4, "14"),
Tolerance("righthole3", true, zfunc, "fronthole", true, 0, -0.2, 0.2, "15")]

## Constructing and solving the optimization problem
## Constructing and solving the optimization problem
pard = Dict("minAllowance"=>0.5, "OptimizeForToleranceCenter"=>true,
"UseTolerances"=>true, "maxPlaneZAllowance"=>1)

mop = MultiOperationProblem(partzeros, holes, planes, tolerances, pard)

import Ipopt

@test mop.opresult.status == "empty"
@test BLC.getfeaturebyname(mop, "fronthole") === holes[1]
@test BLC.getfeaturebyname(mop, "frontface") === planes[1]
@test BLC.problemtype(mop) == :PrimitiveProblem

optimizeproblem!(mop, Ipopt.Optimizer)
@test mop.opresult.status == "LOCALLY_SOLVED"

resallowance = minimumallowance(mop)
tolerror = toleranceerror(mop)

@test isapprox(resallowance.radial, 1.48846, atol=0.01)
@test isapprox(resallowance.axial, 0.5, atol=0.01)
# atol=0.01 -> toleranceerror returns in the 0-100% range
@test isapprox(tolerror, 0, atol=0.01)

# infeasible problem
pard = Dict("minAllowance"=>0.5, "OptimizeForToleranceCenter"=>true,
"UseTolerances"=>true, "maxPlaneZAllowance"=>0.1)

setparameters!(mop, pard)
optimizeproblem!(mop, Ipopt.Optimizer)
@test mop.opresult.status == "LOCALLY_INFEASIBLE"
@test mop.opresult.minallowance === NaN


end

0 comments on commit c5c526f

Please sign in to comment.