function stats = plot_compare_heatmaps(gridCom, gridOpt, z0, outPng) %PLOT_COMPARE_HEATMAPS Side-by-side COMSOL / OpticsFEM / difference heatmaps. Zcom = gridCom.Z; Zopt = gridOpt.Z; valid = ~isnan(Zcom) & ~isnan(Zopt); Zdiff = nan(size(Zcom)); Zdiff(valid) = Zopt(valid) - Zcom(valid); cmin = min([Zcom(valid); Zopt(valid)]); cmax = max([Zcom(valid); Zopt(valid)]); fig = figure('Color', 'w', 'Position', [80 80 1280 420]); subplot(1, 3, 1); imagesc(gridCom.X(1, :), gridCom.Y(:, 1), Zcom); set(gca, 'YDir', 'normal'); axis equal tight; title(sprintf('COMSOL |E| (z=%.2f m)', z0)); xlabel('x (m)'); ylabel('y (m)'); colormap(gca, jet); colorbar; caxis([cmin cmax]); subplot(1, 3, 2); imagesc(gridOpt.X(1, :), gridOpt.Y(:, 1), Zopt); set(gca, 'YDir', 'normal'); axis equal tight; title(sprintf('OpticsFEM |E| (z=%.2f m)', z0)); xlabel('x (m)'); ylabel('y (m)'); colormap(gca, jet); colorbar; caxis([cmin cmax]); subplot(1, 3, 3); imagesc(gridCom.X(1, :), gridCom.Y(:, 1), Zdiff); set(gca, 'YDir', 'normal'); axis equal tight; title('Difference (OpticsFEM - COMSOL)'); xlabel('x (m)'); ylabel('y (m)'); colormap(gca, jet); colorbar; sgtitle(sprintf('normE comparison on z = %.2f m plane', z0), 'FontWeight', 'bold'); if nargin >= 4 && ~isempty(outPng) outDir = fileparts(outPng); if ~isempty(outDir) && ~isfolder(outDir) mkdir(outDir); end exportgraphics(fig, outPng, 'Resolution', 180); fprintf('Saved figure: %s\n', outPng); end stats.maxAbsDiff = max(abs(Zdiff(valid))); stats.relL2 = norm(Zdiff(valid)) / norm(Zcom(valid)); stats.comMin = min(Zcom(valid)); stats.comMax = max(Zcom(valid)); stats.optMin = min(Zopt(valid)); stats.optMax = max(Zopt(valid)); fprintf('COMSOL |E|: [%.4f, %.4f] V/m\n', stats.comMin, stats.comMax); fprintf('OpticsFEM |E|: [%.4f, %.4f] V/m\n', stats.optMin, stats.optMax); fprintf('max|diff|=%.4e, relL2=%.4e\n', stats.maxAbsDiff, stats.relL2); end