We would like to design a cup that is shaped like a tapered cylinder. The cup is to be of the following dimensions:
$$height = 100\text{ mm}$$ $$volume = 300\text{ mL}$$
The radii can only have integer milimeters, even if up to $10 \text{ mL}$ of the volume is lost. What should the upper and lower radii be?
It aims to explore the following:
This is typical application when evaluating a function of two variables $f(x,y)$ such as the volume of a tapered cylinder:
$$V(r,R)=\frac{\pi l}{3}(R^2+Rr+r^2)$$
See previous posts for volume calculation.
from math import*
import itertools
# Looking for solutions in range(x1,x2)
r=[j for j in range(18,42)]
R=r
g=itertools.product(r,R) # this is like a nested for-loop
l=100 #mm
rr,RR,VV=[],[],[] #lists: rr for the small radius, RR for the large radius, VV for volume
vdiff=[]
for k in g:
r=k[0] #element
rr.append(r) #list
R=k[1]
RR.append(R)
V=pi*l/3*(R**2+r*R+r**2) ## mm^3
VV.append(V)
vdiff.append(abs(V/1000-300)) #deviation from desired volume
#print(' {:6.2f} {:6.2f} {:6.2f} {:6.2f}'.format(r,R,V/1000,abs(V/1000-300))) #mL
Searching with the list.index() method returns the first value that satifies the condition you set. Which is good for things like getting the closest point to the desired volume.
# closest point to desired volume
condition=min(vdiff)
index=vdiff.index(condition)
print('\nThe closest value to 300 mL is:')
print('r={} mm R={} mm V={:6.2f} mL'.format(rr[index],RR[index],VV[index]/1000))
Be careful, it only returns the first index that satisfy a value.
So, what if we want multiple values, such as all values within 10 mL from the desired volume?
print('Total datapoints')
print(len(vdiff))
indices=[]
for j in vdiff:
if j<10: #"condition"
indices.append(vdiff.index(j)) #get the index that satisfies "condition" in vdiff
vdiff[vdiff.index(j)]=vdiff[vdiff.index(j)]+1000 #altered the value so i can search for the next index
for j in indices:
vdiff[j]=vdiff[j]-1000 #restored altered value to what it was
datapoints=[]
for j in indices:
datapoint=(j,rr[j],RR[j],VV[j]/1000,vdiff[j]) #get the datapoints at the indices that satisfy "condition"
datapoints.append(datapoint)
print('Total datapoints satisfying "condition"')
s=set(datapoints)
if (len(s)==len(datapoints)):
print('{} unique points'.format(len(s)))
else:
print('check your data!')
#for j in datapoints: # to view the data
#print(j)
import matplotlib.pyplot as plt
# extract datapoints
x=[]
y=[]
for u in datapoints:
x.append(u[1])
y.append(u[2])
x=[u[1] for u in datapoints]
y=[u[2] for u in datapoints]
# plot 1
plt.plot(x,y,'o')
plt.grid()
plt.xlabel('r')
plt.ylabel('R')
plt.show()
What if we want to know the exact solution at $r$?
from scipy.optimize import root
def f(R):
return pi*l/3*(R**2+R*r+r**2)-300000
y2=[]
for u in datapoints:
r=u[1]
sol=root(f,20) #solves the quadratic eq
y2.append(sol.x[0]) #takes the positive root
# plot 2
plt.plot(x,y,'o')
plt.plot(x,y2,'-*r')
plt.grid()
plt.xlabel('r, mm')
plt.ylabel('R, mm')
plt.xticks(range(19,42))
plt.yticks(range(19,42))
plt.show()