""" AC Analysis Example This example demonstrates using the pyngspice Python extension to perform AC analysis (frequency sweep) on an RC filter. """ try: from pyngspice import SimRunner, RawRead except ImportError: print("pyngspice extension not installed. Run: pip install -e .") exit(1) import numpy as np import os # RC filter netlist with AC analysis netlist_content = """ * RC Low-Pass Filter - AC Analysis * Shows magnitude and phase response V1 in 0 AC 1 R1 in out 1k C1 out 0 100n .ac dec 100 1 100k .end """ def main(): output_dir = "./output" os.makedirs(output_dir, exist_ok=True) netlist_file = os.path.join(output_dir, "ac_filter.net") with open(netlist_file, "w") as f: f.write(netlist_content) print("Running AC analysis...") runner = SimRunner(output_folder=output_dir) raw_file, log_file = runner.run_now(netlist_file) raw = RawRead(raw_file) print(f"Analysis type: {raw.analysis_type}") print(f"Complex data: {raw.is_complex}") print(f"\nTraces: {raw.get_trace_names()}") # Get frequency and output voltage (complex) freq = raw.get_trace("frequency").get_wave(0) v_out_complex = raw.get_trace("V(out)").get_wave_complex(0) # Calculate magnitude in dB and phase in degrees magnitude_db = 20 * np.log10(np.abs(v_out_complex)) phase_deg = np.angle(v_out_complex, deg=True) # Find -3dB cutoff frequency idx_3db = np.argmin(np.abs(magnitude_db - (-3))) f_cutoff = freq[idx_3db] print(f"\n-3dB cutoff frequency: {f_cutoff:.2f} Hz") print(f"Theoretical: {1/(2*np.pi*1e3*100e-9):.2f} Hz") # Plot if matplotlib available try: import matplotlib.pyplot as plt fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8), sharex=True) ax1.semilogx(freq, magnitude_db) ax1.axhline(-3, color='r', linestyle='--', alpha=0.5, label='-3dB') ax1.axvline(f_cutoff, color='r', linestyle='--', alpha=0.5) ax1.set_ylabel('Magnitude (dB)') ax1.set_title('RC Low-Pass Filter Frequency Response') ax1.legend() ax1.grid(True, which='both', alpha=0.3) ax2.semilogx(freq, phase_deg) ax2.axhline(-45, color='r', linestyle='--', alpha=0.5, label='-45°') ax2.set_xlabel('Frequency (Hz)') ax2.set_ylabel('Phase (degrees)') ax2.legend() ax2.grid(True, which='both', alpha=0.3) plt.tight_layout() plot_file = os.path.join(output_dir, "ac_filter.png") plt.savefig(plot_file, dpi=150) print(f"\nPlot saved to: {plot_file}") plt.show() except ImportError: print("\nInstall matplotlib for plotting: pip install matplotlib") if __name__ == "__main__": main()