Advertisement
Guest User

COMSOL

a guest
Apr 11th, 2025
50
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.64 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. import sys
  3. import pandas as pd
  4. import numpy as np
  5. import matplotlib.pyplot as plt
  6. from io import StringIO
  7. from scipy.interpolate import griddata
  8.  
  9. def parse_comsol_complex(s):
  10.     """
  11.    Convert a COMSOL-style complex string (e.g. '1.2E-5-3.4E-6i')
  12.    into a Python complex number.
  13.    Returns 0j if s is blank or '0'.
  14.    """
  15.     s = s.strip()
  16.     if s == '' or s == '0':
  17.         return 0 + 0j
  18.     s = s.replace('i', 'j')
  19.     return complex(s)
  20.  
  21. def main(doPlot = True, csv_file = R"C:\Users\winga\Desktop\UCLA\Research\COMSOL\15Pert\37.955GHz O MODE\COMSOL_OUTPUT_37.9GHZ_15.0000.csv",freq = "37.955",x1 = 2.3644,x2=2.404,y1=-0.070778,y2=0.070778):
  22.    
  23.     # -------------------------------
  24.     # Step 1: Read and parse the COMSOL CSV file
  25.     # -------------------------------
  26.     with open(csv_file, "r", encoding="utf-8") as f:
  27.         lines = f.readlines()
  28.  
  29.     metadata_lines = []
  30.     header_line = None
  31.     data_lines = []
  32.     for line in lines:
  33.         stripped = line.strip()
  34.         if not stripped:
  35.             continue
  36.         if header_line is None:
  37.             if stripped.startswith('% X'):
  38.                 header_line = stripped
  39.             else:
  40.                 metadata_lines.append(stripped)
  41.         else:
  42.             data_lines.append(stripped)
  43.  
  44.     # (Optional) Parse metadata
  45.     expected_keys = ["Model", "Version", "Date", "Dimension",
  46.                      "Nodes", "Expressions", "Description", "Length unit"]
  47.     metadata = {}
  48.     for meta_line in metadata_lines:
  49.         if meta_line.startswith('%'):
  50.             clean_line = meta_line.lstrip('%').strip()
  51.             parts = clean_line.split(',', 1)
  52.             if len(parts) == 2:
  53.                 key = parts[0].strip()
  54.                 value = parts[1].strip()
  55.                 if key in expected_keys:
  56.                     metadata[key] = value
  57.     print("Parsed Metadata:")
  58.     for key in expected_keys:
  59.         print(f"{key}: {metadata.get(key, 'Not Found')}")
  60.  
  61.     if header_line is None:
  62.         raise ValueError("No data header line found (starting with '% X').")
  63.     clean_header = header_line.lstrip('%').strip()
  64.     header_cols = clean_header.split(',')
  65.     print("\nData columns (from header):", header_cols)
  66.  
  67.     combined_csv = StringIO()
  68.     combined_csv.write(",".join(header_cols) + "\n")
  69.     for dl in data_lines:
  70.         combined_csv.write(dl + "\n")
  71.     combined_csv.seek(0)
  72.  
  73.     df = pd.read_csv(combined_csv, sep=",")
  74.     print("\nDataFrame head:")
  75.     print(df.head())
  76.  
  77.     # -------------------------------
  78.     # Step 2: Compute the E-field magnitude
  79.     # -------------------------------
  80.     ex_col = "emw.Ex (V/m) @ freq="+freq
  81.     ey_col = "emw.Ey (V/m) @ freq="+freq
  82.     ez_col = "emw.Ez (V/m) @ freq="+freq
  83.     df["Ex"] = df[ex_col].apply(parse_comsol_complex)
  84.     df["Ey"] = df[ey_col].apply(parse_comsol_complex)
  85.     df["Ez"] = df[ez_col].apply(parse_comsol_complex)
  86.  
  87.     df["E_mag"] = np.sqrt(np.abs(df["Ex"])**2 +
  88.                           np.abs(df["Ey"])**2 )
  89.                           #np.abs(df["Ez"])**2)
  90.     print("E_mag: min =", np.min(df["E_mag"]), "max =", np.max(df["E_mag"]))
  91.  
  92.     # -------------------------------
  93.     # Step 3: Interpolate scattered E-field data onto a regular 2D grid
  94.     # -------------------------------
  95.     points = df[['X', 'Y']].values
  96.     values = df['E_mag'].values
  97.  
  98.     x_min, x_max = df['X'].min(), df['X'].max()
  99.     y_min, y_max = df['Y'].min(), df['Y'].max()
  100.     print(f"X domain: {x_min} to {x_max} m")
  101.     print(f"Y domain: {y_min} to {y_max} m")
  102.  
  103.     # Use a moderate grid resolution for memory efficiency:
  104.     n_grid_x = 3000
  105.     n_grid_y = 3000
  106.     print(f"Using grid resolution: {n_grid_x} x {n_grid_y}")
  107.  
  108.     grid_x = np.linspace(x_min, x_max, n_grid_x)
  109.     grid_y = np.linspace(y_min, y_max, n_grid_y)
  110.     grid_X, grid_Y = np.meshgrid(grid_x, grid_y)
  111.  
  112.     E2D = griddata(points, values, (grid_X, grid_Y), method='linear')
  113.     E2D = np.nan_to_num(E2D)
  114.     print("Interpolated grid shape:", E2D.shape)
  115.     print("Interpolated E2D: min =", np.min(E2D), "max =", np.max(E2D))
  116.  
  117.     # -------------------------------
  118.     # Step 4: Define the port and an offset line for integration
  119.     # -------------------------------
  120.     # Given port endpoints:
  121.     xp1 = x1    # 2.3423 m
  122.     yp1 = y1 # -0.0466 m
  123.     xp2 = x2    # 2.3645 m
  124.     yp2 = y2  # 0.0466 m
  125.     p1 = np.array([xp1, yp1])
  126.     p2 = np.array([xp2, yp2])
  127.    
  128.     port_vec = p2 - p1
  129.     L_port = np.linalg.norm(port_vec)
  130.     print(f"Port length = {L_port:.4f} m")
  131.    
  132.     # Unit vector along the port:
  133.     u_port = port_vec / L_port
  134.     # Choose a normal that points in the direction of the backscattered beam.
  135.     # Previously we set: u_norm = [u_port[1], -u_port[0]]
  136.     u_norm = np.array([u_port[1], -u_port[0]])
  137.     # If needed, flip sign: u_norm = -u_norm
  138.  
  139.     # Define an offset distance d_offset (in m) to shift the integration line behind the port.
  140.     d_offset = 0.005  # e.g. 5 mm behind the port
  141.     p1_offset = p1 + d_offset * u_norm
  142.     p2_offset = p2 + d_offset * u_norm
  143.  
  144.     # Parameterize the offset line:
  145.     npts_line = 200
  146.     s_array = np.linspace(0, 1, npts_line)
  147.     line_points = np.array([p1_offset + s*(p2_offset - p1_offset) for s in s_array])
  148.    
  149.     # -------------------------------
  150.     # Step 5: Perform a line integral along the offset line
  151.     # -------------------------------
  152.     # For a free-space wave, the time-averaged Poynting flux is:
  153.     # S = 0.5 * eps0 * c * |E|^2   (W/m²)
  154.     # In 2D, if the simulation is per unit out-of-plane length, then integrating S along a line gives the total power (W).
  155.     eps0 = 8.854187817e-12  # F/m
  156.     c = 3.0e8               # m/s
  157.  
  158.     # For each point on the line, interpolate E2D from the grid:
  159.     E_line = griddata(points, values, (line_points[:,0], line_points[:,1]), method='linear')
  160.     E_line = np.nan_to_num(E_line)
  161.     S_line = 0.5 * eps0 * c * (E_line**2)
  162.    
  163.     # The differential element along the line:
  164.     dl = L_port / (npts_line - 1)
  165.     total_power_line = np.sum(S_line) * dl
  166.     print(f"\nIntegrated power crossing the offset line: {total_power_line:.3e} W")
  167.    
  168.     # -------------------------------
  169.     # Step 6: Plot the results
  170.     # -------------------------------
  171.     # Plot the interpolated E-field heatmap and overlay the port and offset line.
  172.     width = x_max - x_min
  173.     height = y_max - y_min
  174.     aspect_ratio = width / height
  175.  
  176.     plt.figure(figsize=(8,6))
  177.     im = plt.imshow(E2D, origin="lower",
  178.                     extent=[x_min, x_max, y_min, y_max],
  179.                     cmap="jet", vmin=np.min(E2D), vmax=np.max(E2D))
  180.     plt.gca().set_aspect(aspect_ratio)
  181.     plt.colorbar(im, label="|E| (V/m)")
  182.     plt.xlabel("X [m]")
  183.     plt.ylabel("Y [m]")
  184.     plt.title("Backscattered Electric Field Magnitude")
  185.    
  186.     # Draw the original port line and the offset line
  187.     plt.plot([p1[0], p2[0]], [p1[1], p2[1]], 'w-', linewidth=3, label="Port")
  188.     plt.plot([p1_offset[0], p2_offset[0]], [p1_offset[1], p2_offset[1]],
  189.              'w--', linewidth=3, label="Offset integration line")
  190.    
  191.     plt.legend()
  192.     plt.savefig('lineint.jpg',dpi=300)
  193.     if doPlot:
  194.         plt.show()
  195.    
  196.     # Additionally, plot the Poynting flux along the offset line
  197.     plt.figure(figsize=(8,5))
  198.     plt.plot(s_array * L_port, S_line, 'bo-', linewidth=2, markersize=4)
  199.     plt.xlabel("Distance along offset line [m]")
  200.     plt.ylabel("Poynting flux S [W/m²]")
  201.     plt.title("Poynting Flux along the Offset Integration Line")
  202.     plt.grid(True)
  203.     plt.savefig('pynting.jpg',dpi=300)
  204.  
  205.     if doPlot:
  206.         plt.show()
  207.     return total_power_line
  208.  
  209. if __name__ == "__main__":
  210.     main()
  211.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement