import json, math, random import numpy as np import torch from scipy.linalg import eigh SEED=2950 np.random.seed(SEED); random.seed(SEED); torch.manual_seed(SEED) def beam(nel=8, L=1.0, EI=1.0, rhoA=1.0): h=L/nel; K=np.zeros((2*(nel+1),2*(nel+1))); M=K.copy() ke=EI/h**3*np.array([[12,6*h,-12,6*h],[6*h,4*h*h,-6*h,2*h*h],[-12,-6*h,12,-6*h],[6*h,2*h*h,-6*h,4*h*h]],float) me=rhoA*h/420*np.array([[156,22*h,54,-13*h],[22*h,4*h*h,13*h,-3*h*h],[54,13*h,156,-22*h],[-13*h,-3*h*h,-22*h,4*h*h]],float) for e in range(nel): ix=[2*e,2*e+1,2*e+2,2*e+3]; K[np.ix_(ix,ix)]+=ke; M[np.ix_(ix,ix)]+=me # clamped left, free right return K[2:,2:], M[2:,2:] def inertia(K,M,w,tol=1e-12): # For positive-definite M, inertia(K-w^2 M) equals count of frequencies below w. A=K-(w*w)*M; ev=np.linalg.eigvalsh(A); scale=max(1.,np.max(np.abs(A))) return int(np.sum(ev < -tol*scale)) def bracket(K,M,target,hi=None,tol=1e-3): vals=eigh(K,M,eigvals_only=True); freqs=np.sqrt(np.maximum(vals,0)) if hi is None: hi=float(freqs[-1]*1.05) lo=0.; upper=hi # Find the transition count target-1 -> target; bisection around exact transition. grid=np.linspace(0,upper,400) for a,b in zip(grid[:-1],grid[1:]): if inertia(K,M,a)==target-1 and inertia(K,M,b)>=target: lo,upper=float(a),float(b); break # narrow an interval whose endpoints have counts target-1 and target while upper-lo>tol: mid=(lo+upper)/2 if inertia(K,M,mid)=0)); changes=int(np.sum(np.diff(counts)>0)) lo,up,freqs=bracket(K,M,target) contained=int(np.sum((freqs>lo)&(freqs