import%20marimo%0A%0A__generated_with%20%3D%20%220.23.16%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Planar%20Truss%20Sizing%20under%20Elastic%20Equilibrium%0A%0A%20%20%20%20A%20structural%20designer%20allocates%20material%20among%20the%20members%20of%20a%20planar%0A%20%20%20%20truss%20before%20the%20structure%20deforms%20under%20load.%20The%20design%20must%20anticipate%0A%20%20%20%20elastic%20equilibrium%3A%20changing%20a%20member%20area%20changes%20the%20stiffness%2C%20which%0A%20%20%20%20redistributes%20both%20displacement%20and%20internal%20force%20throughout%20the%20truss.%0A%0A%20%20%20%20This%20example%20sizes%20a%20two-bay%20cantilever.%20The%20upper%20problem%20minimizes%0A%20%20%20%20compliance%20subject%20to%20a%20material%20budget%2C%20while%20the%20lower%20problem%20finds%20the%0A%20%20%20%20displacement%20that%20minimizes%20total%20potential%20energy.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20from%20pathlib%20import%20Path%0A%0A%20%20%20%20import%20cvxpy%20as%20cp%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20numpy%20as%20np%0A%0A%20%20%20%20from%20blvpy%20import%20BilevelProblem%2C%20LowerProblem%0A%0A%20%20%20%20plt.style.use(Path(__file__).resolve().parents%5B1%5D%20%2F%20%22_shared%22%20%2F%20%22zhlatex.mplstyle%22)%0A%20%20%20%20return%20BilevelProblem%2C%20LowerProblem%2C%20Path%2C%20cp%2C%20mo%2C%20np%2C%20plt%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Bilevel%20formulation%0A%0A%20%20%20%20Suppose%20the%20truss%20has%20%24n%24%20members%20and%20%24m%24%20nodal%20displacement%20degrees%20of%0A%20%20%20%20freedom.%20Let%20%24a%2CL%5Cin%5Cmathbf%7BR%7D%5En%24%20collect%20the%20member%20areas%20and%20lengths%2C%20and%0A%20%20%20%20let%20the%20scalar%20%24E%3E0%24%20be%20the%20Young's%20modulus%20shared%20by%20all%20members.%20The%0A%20%20%20%20vectors%20%24u%2Cf%5Cin%5Cmathbf%7BR%7D%5Em%24%20collect%20nodal%20displacements%20and%20applied%20nodal%0A%20%20%20%20forces.%20The%20compatibility%20matrix%20%24B%5Cin%5Cmathbf%7BR%7D%5E%7Bn%5Ctimes%20m%7D%24%20maps%20nodal%0A%20%20%20%20displacements%20to%20axial%20member%20elongations%2C%20so%20its%20%24e%24th%20component%20is%0A%20%20%20%20%24(Bu)_e%3Db_e%5ETu%24.%0A%0A%20%20%20%20The%20designer%20chooses%20the%20areas%20while%20anticipating%20elastic%20equilibrium%3A%0A%0A%20%20%20%20%5C%5B%0A%20%20%20%20%5Cbegin%7Barray%7D%7Bll%7D%0A%20%20%20%20%5Cmathop%7B%5Cmathrm%7Bminimize%7D%7D_%7Ba%2Cu%7D%20%26%20f%5ETu%5C%5C%0A%20%20%20%20%5Cmathop%7B%5Cmathrm%7Bsubject%5C%20to%7D%7D%0A%20%20%20%20%20%20%20%20%26%20a_%7B%5Cmin%7D%5Cmathbf%7B1%7D%5Cpreceq%20a%5Cpreceq%20a_%7B%5Cmax%7D%5Cmathbf%7B1%7D%2C%5C%5C%0A%20%20%20%20%20%20%20%20%26%20L%5ETa%5Cleq%20V_%7B%5Cmax%7D%2C%5C%5C%0A%20%20%20%20%20%20%20%20%26%20u%5Cin%20S(a).%0A%20%20%20%20%5Cend%7Barray%7D%0A%20%20%20%20%5C%5D%0A%0A%20%20%20%20Here%20%240%3Ca_%7B%5Cmin%7D%5Cleq%20a_%7B%5Cmax%7D%24%20bound%20each%20cross-sectional%20area%2C%20and%0A%20%20%20%20%24V_%7B%5Cmax%7D%3E0%24%20is%20the%20material-volume%20budget%20because%20member%20%24e%24%20has%20volume%0A%20%20%20%20%24L_ea_e%24.%20For%20the%20prescribed%20load%20%24f%24%2C%20the%20objective%20%24f%5ETu%24%20is%20compliance%3A%0A%20%20%20%20smaller%20compliance%20means%20a%20stiffer%20structure.%0A%0A%20%20%20%20For%20an%20area%20allocation%20%24a%24%2C%20the%20assembled%20stiffness%20matrix%20is%0A%0A%20%20%20%20%5C%5B%0A%20%20%20%20K(a)%3DB%5ET%5Cmathop%7B%5Crm%20diag%7D%5Cleft(%0A%20%20%20%20%20%20%20%20%5Cfrac%7BEa_1%7D%7BL_1%7D%2C%5Cldots%2C%5Cfrac%7BEa_n%7D%7BL_n%7D%0A%20%20%20%20%5Cright)B.%0A%20%20%20%20%5C%5D%0A%0A%20%20%20%20Let%20%24%5Cmathcal%20F%24%20contain%20the%20fixed%20displacement%20indices.%20The%20lower-level%20elasticity%20problem%20is%0A%0A%20%20%20%20%5C%5B%0A%20%20%20%20%5Cbegin%7Barray%7D%7Bll%7D%0A%20%20%20%20S(a)%3D%5Cmathop%7B%5Cmathrm%7Bargmin%7D%7D_u%0A%20%20%20%20%20%20%20%20%26%20%5Cdisplaystyle%5Cfrac%7B1%7D%7B2%7Du%5ETK(a)u-f%5ETu%5C%5C%0A%20%20%20%20%5Cmathop%7B%5Cmathrm%7Bsubject%5C%20to%7D%7D%0A%20%20%20%20%20%20%20%20%26%20u_i%3D0%2C%5Cqquad%20i%5Cin%5Cmathcal%20F.%0A%20%20%20%20%5Cend%7Barray%7D%0A%20%20%20%20%5C%5D%0A%0A%20%20%20%20This%20is%20the%20minimum-total-potential-energy%20principle%20for%20a%20pin-jointed%2C%0A%20%20%20%20small-displacement%2C%20linear-elastic%20truss.%20The%20strain-energy%20term%20also%20has%0A%20%20%20%20the%20equivalent%20memberwise%20form%0A%0A%20%20%20%20%5C%5B%0A%20%20%20%20%5Cfrac%7B1%7D%7B2%7Du%5ETK(a)u%0A%20%20%20%20%3D%5Cfrac%7B1%7D%7B2%7D%5Csum_%7Be%3D1%7D%5En%5Cfrac%7BEa_e%7D%7BL_e%7D(b_e%5ETu)%5E2.%0A%20%20%20%20%5C%5D%0A%0A%20%20%20%20Partitioning%20the%20displacement%20into%20free%20and%20fixed%20components%20gives%0A%0A%20%20%20%20%5C%5B%0A%20%20%20%20K_%7B%5Crm%20free%2Cfree%7D(a)u_%7B%5Crm%20free%7D%3Df_%7B%5Crm%20free%7D%2C%0A%20%20%20%20%5Cqquad%20u_%7B%5Crm%20fixed%7D%3D0.%0A%20%20%20%20%5C%5D%0A%0A%20%20%20%20Thus%20the%20optimization%20enforces%20ordinary%20static%20equilibrium.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Two-bay%20cantilever%0A%0A%20%20%20%20Six%20nodes%20form%20two%20square%20bays%20with%20crossed%20diagonals.%20Both%20degrees%20of%0A%20%20%20%20freedom%20at%20the%20two%20left%20nodes%2C%20numbered%200%20and%201%2C%20are%20fixed.%20With%0A%20%20%20%20%24u%3D(u_%7B0x%7D%2Cu_%7B0y%7D%2Cu_%7B1x%7D%2Cu_%7B1y%7D%2C%5Cldots)%24%2C%20this%20means%0A%20%20%20%20%24%5Cmathcal%20F%3D%5C%7B0%2C1%2C2%2C3%5C%7D%24.%20A%20unit%20downward%20load%20acts%20at%20the%20lower%20tip%20and%20a%0A%20%20%20%20smaller%20downward%20load%20acts%20at%20the%20upper%20tip.%20All%20quantities%20are%20normalized%3A%0A%20%20%20%20%24E%3D10%24%2C%20%240.08%5Cleq%20a_e%5Cleq2%24%2C%20and%20the%20material%20budget%20is%20%2465%5C%25%24%20of%20the%0A%20%20%20%20volume%20obtained%20by%20assigning%20unit%20area%20to%20every%20member.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(np)%3A%0A%20%20%20%20nodes%20%3D%20np.array(%0A%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B0.0%2C%200.0%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B0.0%2C%201.0%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B1.0%2C%200.0%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B1.0%2C%201.0%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B2.0%2C%200.0%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B2.0%2C%201.0%5D%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20)%0A%20%20%20%20members%20%3D%20np.array(%0A%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B0%2C%202%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B1%2C%203%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B2%2C%203%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B0%2C%203%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B1%2C%202%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B2%2C%204%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B3%2C%205%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B4%2C%205%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B2%2C%205%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B3%2C%204%5D%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20)%0A%0A%20%20%20%20n_nodes%20%3D%20nodes.shape%5B0%5D%0A%20%20%20%20n%20%3D%20members.shape%5B0%5D%0A%20%20%20%20m%20%3D%202%20*%20n_nodes%0A%20%20%20%20B%20%3D%20np.zeros((n%2C%20m))%0A%20%20%20%20L%20%3D%20np.zeros(n)%0A%20%20%20%20for%20member_index%2C%20(start_node%2C%20end_node)%20in%20enumerate(members)%3A%0A%20%20%20%20%20%20%20%20direction%20%3D%20nodes%5Bend_node%5D%20-%20nodes%5Bstart_node%5D%0A%20%20%20%20%20%20%20%20L%5Bmember_index%5D%20%3D%20np.linalg.norm(direction)%0A%20%20%20%20%20%20%20%20direction%20%2F%3D%20L%5Bmember_index%5D%0A%20%20%20%20%20%20%20%20B%5Bmember_index%2C%202%20*%20start_node%20%3A%202%20*%20start_node%20%2B%202%5D%20%3D%20-direction%0A%20%20%20%20%20%20%20%20B%5Bmember_index%2C%202%20*%20end_node%20%3A%202%20*%20end_node%20%2B%202%5D%20%3D%20direction%0A%0A%20%20%20%20f%20%3D%20np.zeros(m)%0A%20%20%20%20f%5B2%20*%204%20%2B%201%5D%20%3D%20-1.0%0A%20%20%20%20f%5B2%20*%205%20%2B%201%5D%20%3D%20-0.25%0A%20%20%20%20fixed_nodes%20%3D%20np.array(%5B0%2C%201%5D)%0A%20%20%20%20F%20%3D%20np.column_stack((2%20*%20fixed_nodes%2C%202%20*%20fixed_nodes%20%2B%201)).ravel()%0A%0A%20%20%20%20E%20%3D%2010.0%0A%20%20%20%20a_min%20%3D%200.08%0A%20%20%20%20a_max%20%3D%202.0%0A%20%20%20%20V_max%20%3D%200.65%20*%20np.sum(L)%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20B%2C%0A%20%20%20%20%20%20%20%20E%2C%0A%20%20%20%20%20%20%20%20F%2C%0A%20%20%20%20%20%20%20%20L%2C%0A%20%20%20%20%20%20%20%20V_max%2C%0A%20%20%20%20%20%20%20%20a_max%2C%0A%20%20%20%20%20%20%20%20a_min%2C%0A%20%20%20%20%20%20%20%20f%2C%0A%20%20%20%20%20%20%20%20fixed_nodes%2C%0A%20%20%20%20%20%20%20%20m%2C%0A%20%20%20%20%20%20%20%20members%2C%0A%20%20%20%20%20%20%20%20n%2C%0A%20%20%20%20%20%20%20%20n_nodes%2C%0A%20%20%20%20%20%20%20%20nodes%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Specify%20and%20solve%20the%20model%0A%0A%20%20%20%20%60a%60%20is%20an%20upper-level%20variable%20and%20is%20listed%20as%20a%20lower-problem%0A%20%20%20%20parameter%2C%20which%20is%20therefore%20fixed%20when%20equilibrium%20is%20computed.%0A%20%20%20%20A%20uniform%20feasible%20design%20supplies%20the%20nonlinear%20solver's%0A%20%20%20%20initial%20point.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20B%2C%0A%20%20%20%20BilevelProblem%2C%0A%20%20%20%20E%2C%0A%20%20%20%20F%2C%0A%20%20%20%20L%2C%0A%20%20%20%20LowerProblem%2C%0A%20%20%20%20V_max%2C%0A%20%20%20%20a_max%2C%0A%20%20%20%20a_min%2C%0A%20%20%20%20cp%2C%0A%20%20%20%20f%2C%0A%20%20%20%20m%2C%0A%20%20%20%20n%2C%0A%20%20%20%20np%2C%0A)%3A%0A%20%20%20%20a%20%3D%20cp.Variable(%0A%20%20%20%20%20%20%20%20n%2C%0A%20%20%20%20%20%20%20%20nonneg%3DTrue%2C%0A%20%20%20%20%20%20%20%20bounds%3D%5Ba_min%2C%20a_max%5D%2C%0A%20%20%20%20%20%20%20%20name%3D%22a%22%2C%0A%20%20%20%20)%0A%20%20%20%20u%20%3D%20cp.Variable(m%2C%20name%3D%22u%22)%0A%20%20%20%20a.value%20%3D%20np.full(n%2C%20V_max%20%2F%20np.sum(L))%0A%0A%20%20%20%20stiffness_weights%20%3D%20cp.multiply(E%20%2F%20L%2C%20a)%0A%20%20%20%20strain_energy%20%3D%200.5%20*%20cp.sum(cp.multiply(stiffness_weights%2C%20cp.square(B%20%40%20u)))%0A%20%20%20%20equilibrium%20%3D%20LowerProblem(%0A%20%20%20%20%20%20%20%20cp.Minimize(strain_energy%20-%20f%20%40%20u)%2C%0A%20%20%20%20%20%20%20%20%5Bu%5BF%5D%20%3D%3D%200.0%5D%2C%0A%20%20%20%20%20%20%20%20parameters%3D%5Ba%5D%2C%0A%20%20%20%20)%0A%20%20%20%20problem%20%3D%20BilevelProblem(%0A%20%20%20%20%20%20%20%20cp.Minimize(f%20%40%20u)%2C%0A%20%20%20%20%20%20%20%20equilibrium%2C%0A%20%20%20%20%20%20%20%20upper_constraints%3D%5BL%20%40%20a%20%3C%3D%20V_max%5D%2C%0A%20%20%20%20)%0A%0A%20%20%20%20assert%20problem.is_dblp()%0A%20%20%20%20return%20a%2C%20problem%2C%20u%0A%0A%0A%40app.cell%0Adef%20_(problem)%3A%0A%20%20%20%20epsilon_target%20%3D%201e-5%0A%20%20%20%20result%20%3D%20problem.solve(%0A%20%20%20%20%20%20%20%20epsilon_initial%3D1e-2%2C%0A%20%20%20%20%20%20%20%20epsilon_target%3Depsilon_target%2C%0A%20%20%20%20%20%20%20%20best_of%3D2%2C%0A%20%20%20%20%20%20%20%20seed%3D7%2C%0A%20%20%20%20%20%20%20%20verbose%3DFalse%2C%0A%20%20%20%20)%0A%20%20%20%20diagnostics%20%3D%20problem.gap_diagnostics(result)%0A%20%20%20%20return%20diagnostics%2C%20epsilon_target%2C%20result%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Independent%20elastic-equilibrium%20references%0A%0A%20%20%20%20Two%20ordinary%20CVXPY%20problems%20independently%20resolve%20equilibrium%20at%20fixed%0A%20%20%20%20areas.%20The%20first%20uses%20the%20uniform%20allocation%2C%20providing%20a%20baseline%20with%0A%20%20%20%20the%20same%20material%20volume.%20The%20second%20fixes%20the%20areas%20selected%20by%20BLVPY%20and%0A%20%20%20%20verifies%20its%20lower-level%20displacement%20without%20using%20the%20lifted%20KKT%20model.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(B%2C%20E%2C%20F%2C%20L%2C%20V_max%2C%20a%2C%20cp%2C%20f%2C%20m%2C%20n%2C%20np)%3A%0A%20%20%20%20def%20solve_fixed_equilibrium(a_value)%3A%0A%20%20%20%20%20%20%20%20reference_u%20%3D%20cp.Variable(m)%0A%20%20%20%20%20%20%20%20reference_weights%20%3D%20E%20*%20a_value%20%2F%20L%0A%20%20%20%20%20%20%20%20reference_energy%20%3D%200.5%20*%20cp.sum(cp.multiply(reference_weights%2C%20cp.square(B%20%40%20reference_u)))%0A%20%20%20%20%20%20%20%20reference_problem%20%3D%20cp.Problem(%0A%20%20%20%20%20%20%20%20%20%20%20%20cp.Minimize(reference_energy%20-%20f%20%40%20reference_u)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5Breference_u%5BF%5D%20%3D%3D%200.0%5D%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20reference_problem.solve(%0A%20%20%20%20%20%20%20%20%20%20%20%20solver%3Dcp.CLARABEL%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20tol_gap_abs%3D1e-11%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20tol_gap_rel%3D1e-11%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20tol_feas%3D1e-11%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20assert%20reference_problem.status%20%3D%3D%20cp.OPTIMAL%0A%20%20%20%20%20%20%20%20return%20np.asarray(reference_u.value%2C%20dtype%3Dfloat)%0A%0A%20%20%20%20uniform_a%20%3D%20np.full(n%2C%20V_max%20%2F%20np.sum(L))%0A%20%20%20%20uniform_u%20%3D%20solve_fixed_equilibrium(uniform_a)%0A%20%20%20%20optimized_a%20%3D%20np.asarray(a.value%2C%20dtype%3Dfloat)%0A%20%20%20%20optimized_reference_u%20%3D%20solve_fixed_equilibrium(optimized_a)%0A%0A%20%20%20%20uniform_compliance%20%3D%20float(f%20%40%20uniform_u)%0A%20%20%20%20optimized_reference_compliance%20%3D%20float(f%20%40%20optimized_reference_u)%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20optimized_a%2C%0A%20%20%20%20%20%20%20%20optimized_reference_compliance%2C%0A%20%20%20%20%20%20%20%20optimized_reference_u%2C%0A%20%20%20%20%20%20%20%20uniform_a%2C%0A%20%20%20%20%20%20%20%20uniform_compliance%2C%0A%20%20%20%20%20%20%20%20uniform_u%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(%0A%20%20%20%20F%2C%0A%20%20%20%20L%2C%0A%20%20%20%20V_max%2C%0A%20%20%20%20a_max%2C%0A%20%20%20%20a_min%2C%0A%20%20%20%20diagnostics%2C%0A%20%20%20%20epsilon_target%2C%0A%20%20%20%20f%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20np%2C%0A%20%20%20%20optimized_a%2C%0A%20%20%20%20optimized_reference_compliance%2C%0A%20%20%20%20optimized_reference_u%2C%0A%20%20%20%20result%2C%0A%20%20%20%20u%2C%0A%20%20%20%20uniform_compliance%2C%0A)%3A%0A%20%20%20%20optimized_u%20%3D%20np.asarray(u.value%2C%20dtype%3Dfloat)%0A%20%20%20%20optimized_compliance%20%3D%20float(f%20%40%20optimized_u)%0A%20%20%20%20optimized_volume%20%3D%20float(L%20%40%20optimized_a)%0A%20%20%20%20improvement_percent%20%3D%20100.0%20*%20(uniform_compliance%20-%20optimized_compliance)%20%2F%20uniform_compliance%0A%0A%20%20%20%20assert%20result.succeeded%2C%20result.message%0A%20%20%20%20assert%20np.isfinite(result.objective)%0A%20%20%20%20assert%20result.final_epsilon%20%3C%3D%20epsilon_target%0A%20%20%20%20assert%20result.residuals.max_violation%20%3C%3D%202e-5%0A%20%20%20%20assert%20result.complementarity%20%3C%3D%201.1%20*%20epsilon_target%0A%20%20%20%20assert%20abs(diagnostics.source_gap)%20%3C%3D%201.1%20*%20epsilon_target%0A%20%20%20%20assert%20np.max(np.abs(optimized_u%5BF%5D))%20%3C%3D%201e-7%0A%20%20%20%20assert%20np.min(optimized_a)%20%3E%3D%20a_min%20-%201e-7%0A%20%20%20%20assert%20np.max(optimized_a)%20%3C%3D%20a_max%20%2B%201e-7%0A%20%20%20%20assert%20optimized_volume%20%3C%3D%20V_max%20%2B%201e-7%0A%20%20%20%20assert%20np.allclose(optimized_u%2C%20optimized_reference_u%2C%20atol%3D1e-2)%0A%20%20%20%20assert%20abs(optimized_compliance%20-%20optimized_reference_compliance)%20%3C%3D%201e-2%0A%20%20%20%20assert%20optimized_compliance%20%3C%200.85%20*%20uniform_compliance%0A%0A%20%20%20%20mo.md(rf%22%22%22%0A%20%20%20%20%23%23%20Result%0A%0A%20%20%20%20%7C%20quantity%20%7C%20value%20%7C%0A%20%20%20%20%7C%20---%20%7C%20---%3A%20%7C%0A%20%20%20%20%7C%20status%20%7C%20%60%7Bresult.status%7D%60%20%7C%0A%20%20%20%20%7C%20uniform-design%20compliance%20%7C%20%7Buniform_compliance%3A.6f%7D%20%7C%0A%20%20%20%20%7C%20optimized%20compliance%20%7C%20%7Boptimized_compliance%3A.6f%7D%20%7C%0A%20%20%20%20%7C%20compliance%20improvement%20%7C%20%7Bimprovement_percent%3A.2f%7D%25%20%7C%0A%20%20%20%20%7C%20material%20volume%20%7C%20%7Boptimized_volume%3A.6f%7D%20%2F%20%7BV_max%3A.6f%7D%20%7C%0A%20%20%20%20%7C%20optimized%20areas%20%7C%20%7Bnp.array2string(optimized_a%2C%20precision%3D3)%7D%20%7C%0A%20%20%20%20%7C%20upper%20objective%20%7C%20%7Bfloat(result.objective)%3A.6f%7D%20%7C%0A%20%20%20%20%7C%20final%20epsilon%20%7C%20%7Bresult.final_epsilon%3A.3e%7D%20%7C%0A%20%20%20%20%7C%20maximum%20lifted%20residual%20%7C%20%7Bresult.residuals.max_violation%3A.3e%7D%20%7C%0A%20%20%20%20%7C%20complementarity%20%7C%20%7Bresult.complementarity%3A.3e%7D%20%7C%0A%20%20%20%20%7C%20signed%20source%20gap%20%7C%20%7Bdiagnostics.source_gap%3A.3e%7D%20%7C%0A%0A%20%20%20%20The%20optimized%20design%20reduces%20compliance%20by%20%7Bimprovement_percent%3A.1f%7D%25%0A%20%20%20%20without%20adding%20material.%20It%20thickens%20members%20along%20the%20dominant%20load%20path%0A%20%20%20%20and%20leaves%20only%20the%20minimum%20area%20in%20members%20that%20contribute%20less%20stiffness%0A%20%20%20%20for%20this%20loading%20direction.%20The%20independent%20equilibrium%20solve%20reproduces%0A%20%20%20%20both%20the%20displacement%20and%20compliance%20returned%20by%20BLVPY.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%20(optimized_u%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Load%20path%20and%20deformation%0A%0A%20%20%20%20The%20panels%20compare%20equal-volume%20designs.%20Line%20width%20represents%20member%0A%20%20%20%20area%2C%20and%20color%20represents%20axial%20stress%3A%20blue%20members%20are%20in%20tension%20and%0A%20%20%20%20red%20members%20are%20in%20compression.%20The%20light%20dashed%20lines%20show%20the%20undeformed%0A%20%20%20%20truss%2C%20while%20the%20colored%20lines%20exaggerate%20displacement%20to%20reveal%20its%20mode.%0A%20%20%20%20Loads%20and%20fixed%20supports%20are%20shown%20explicitly.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(B%2C%20E%2C%20L%2C%20n_nodes%2C%20np%2C%20optimized_u%2C%20uniform_u)%3A%0A%20%20%20%20uniform_stress%20%3D%20E%20*%20(B%20%40%20uniform_u)%20%2F%20L%0A%20%20%20%20optimized_stress%20%3D%20E%20*%20(B%20%40%20optimized_u)%20%2F%20L%0A%20%20%20%20maximum_displacement%20%3D%20max(%0A%20%20%20%20%20%20%20%20np.max(np.linalg.norm(uniform_u.reshape(n_nodes%2C%202)%2C%20axis%3D1))%2C%0A%20%20%20%20%20%20%20%20np.max(np.linalg.norm(optimized_u.reshape(n_nodes%2C%202)%2C%20axis%3D1))%2C%0A%20%20%20%20)%0A%20%20%20%20deformation_scale%20%3D%200.32%20%2F%20maximum_displacement%0A%20%20%20%20stress_limit%20%3D%20max(np.max(np.abs(uniform_stress))%2C%20np.max(np.abs(optimized_stress)))%0A%20%20%20%20return%20deformation_scale%2C%20optimized_stress%2C%20stress_limit%2C%20uniform_stress%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(%0A%20%20%20%20Path%2C%0A%20%20%20%20deformation_scale%2C%0A%20%20%20%20f%2C%0A%20%20%20%20fixed_nodes%2C%0A%20%20%20%20members%2C%0A%20%20%20%20nodes%2C%0A%20%20%20%20np%2C%0A%20%20%20%20optimized_a%2C%0A%20%20%20%20optimized_stress%2C%0A%20%20%20%20optimized_u%2C%0A%20%20%20%20plt%2C%0A%20%20%20%20stress_limit%2C%0A%20%20%20%20uniform_a%2C%0A%20%20%20%20uniform_stress%2C%0A%20%20%20%20uniform_u%2C%0A)%3A%0A%20%20%20%20truss_figure%2C%20truss_axes%20%3D%20plt.subplots(1%2C%202%2C%20figsize%3D(8.5%2C%203.4)%2C%20layout%3D%22constrained%22)%0A%20%20%20%20stress_norm%20%3D%20plt.Normalize(vmin%3D-stress_limit%2C%20vmax%3Dstress_limit)%0A%20%20%20%20stress_cmap%20%3D%20plt.get_cmap(%22coolwarm_r%22)%0A%20%20%20%20maximum_area%20%3D%20max(np.max(uniform_a)%2C%20np.max(optimized_a))%0A%0A%20%20%20%20def%20draw_truss(axis%2C%20a_value%2C%20u_value%2C%20member_stress%2C%20title)%3A%0A%20%20%20%20%20%20%20%20deformed_nodes%20%3D%20nodes%20%2B%20deformation_scale%20*%20u_value.reshape(nodes.shape)%0A%20%20%20%20%20%20%20%20for%20start_node%2C%20end_node%20in%20members%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20axis.plot(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nodes%5B%5Bstart_node%2C%20end_node%5D%2C%200%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nodes%5B%5Bstart_node%2C%20end_node%5D%2C%201%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20color%3D%220.78%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20linestyle%3D%22--%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20linewidth%3D0.8%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20zorder%3D1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20for%20member_index%2C%20(start_node%2C%20end_node)%20in%20enumerate(members)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20axis.plot(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20deformed_nodes%5B%5Bstart_node%2C%20end_node%5D%2C%200%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20deformed_nodes%5B%5Bstart_node%2C%20end_node%5D%2C%201%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20color%3Dstress_cmap(stress_norm(member_stress%5Bmember_index%5D))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20linewidth%3D0.7%20%2B%205.0%20*%20a_value%5Bmember_index%5D%20%2F%20maximum_area%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20solid_capstyle%3D%22round%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20zorder%3D2%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20axis.scatter(%0A%20%20%20%20%20%20%20%20%20%20%20%20deformed_nodes%5B%3A%2C%200%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20deformed_nodes%5B%3A%2C%201%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20s%3D15%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3D%22black%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20zorder%3D3%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20axis.scatter(%0A%20%20%20%20%20%20%20%20%20%20%20%20nodes%5Bfixed_nodes%2C%200%5D%20-%200.07%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20nodes%5Bfixed_nodes%2C%201%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20marker%3D%22%3E%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20s%3D80%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20facecolors%3D%22none%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20edgecolors%3D%22black%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20linewidths%3D1.0%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20zorder%3D4%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20for%20node_index%20in%20(4%2C%205)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20load_y%20%3D%20f%5B2%20*%20node_index%20%2B%201%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20loaded_node%20%3D%20deformed_nodes%5Bnode_index%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20arrow_length%20%3D%200.18%20%2B%200.16%20*%20abs(load_y)%0A%20%20%20%20%20%20%20%20%20%20%20%20axis.annotate(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20xy%3Dloaded_node%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20xytext%3D(loaded_node%5B0%5D%2C%20loaded_node%5B1%5D%20%2B%20arrow_length)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20arrowprops%3D%7B%22arrowstyle%22%3A%20%22-%7C%3E%22%2C%20%22color%22%3A%20%22black%22%2C%20%22linewidth%22%3A%201.1%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20zorder%3D5%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20axis.set(%0A%20%20%20%20%20%20%20%20%20%20%20%20xlim%3D(-0.2%2C%202.25)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20ylim%3D(-0.48%2C%201.28)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20aspect%3D%22equal%22%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20axis.set_title(title%2C%20fontsize%3D15)%0A%20%20%20%20%20%20%20%20axis.axis(%22off%22)%0A%0A%20%20%20%20draw_truss(%0A%20%20%20%20%20%20%20%20truss_axes%5B0%5D%2C%0A%20%20%20%20%20%20%20%20uniform_a%2C%0A%20%20%20%20%20%20%20%20uniform_u%2C%0A%20%20%20%20%20%20%20%20uniform_stress%2C%0A%20%20%20%20%20%20%20%20%22Uniform%20sizing%22%2C%0A%20%20%20%20)%0A%20%20%20%20draw_truss(%0A%20%20%20%20%20%20%20%20truss_axes%5B1%5D%2C%0A%20%20%20%20%20%20%20%20optimized_a%2C%0A%20%20%20%20%20%20%20%20optimized_u%2C%0A%20%20%20%20%20%20%20%20optimized_stress%2C%0A%20%20%20%20%20%20%20%20%22Optimized%20sizing%22%2C%0A%20%20%20%20)%0A%0A%20%20%20%20stress_map%20%3D%20plt.cm.ScalarMappable(norm%3Dstress_norm%2C%20cmap%3Dstress_cmap)%0A%20%20%20%20stress_colorbar%20%3D%20truss_figure.colorbar(%0A%20%20%20%20%20%20%20%20stress_map%2C%0A%20%20%20%20%20%20%20%20ax%3Dtruss_axes%2C%0A%20%20%20%20%20%20%20%20orientation%3D%22horizontal%22%2C%0A%20%20%20%20%20%20%20%20fraction%3D0.07%2C%0A%20%20%20%20%20%20%20%20pad%3D0.02%2C%0A%20%20%20%20%20%20%20%20shrink%3D0.65%2C%0A%20%20%20%20)%0A%20%20%20%20stress_colorbar.set_label(%22Axial%20stress%20(tension%20%24%3E0%24)%22%2C%20fontsize%3D13)%0A%20%20%20%20stress_colorbar.ax.tick_params(labelsize%3D13)%0A%0A%20%20%20%20figure_dir%20%3D%20Path(__file__).resolve().parent%20%2F%20%22figures%22%0A%20%20%20%20figure_dir.mkdir(parents%3DTrue%2C%20exist_ok%3DTrue)%0A%20%20%20%20figure_path%20%3D%20figure_dir%20%2F%20%22planar_truss_sizing.pdf%22%0A%20%20%20%20truss_figure.savefig(figure_path%2C%20bbox_inches%3D%22tight%22)%0A%20%20%20%20plt.show()%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
d45e9b7e26128a8deedc6586a35f75e1