function export_phy_to_json(phy, jsonFile, cfg) %EXPORT_PHY_TO_JSON 由 phy + case_config 生成 OpticsFEM 散射 JSON(通用)。 % BoundaryFlag(i) = 网格面域 DomainOfTri 编号 i 的边界类型(0 PMC, 2 SBC, 4 PBC)。 if nargin < 1 || isempty(phy) if ~exist('case_config', 'file') error('未传入 phy,且当前目录无 case_config.m'); end [phy, cfg] = case_config(); if nargin < 2 || isempty(jsonFile) jsonFile = fullfile(pwd, cfg.jsonFile); end elseif nargin < 3 || isempty(cfg) if exist('case_config', 'file') [~, cfg] = case_config(); else cfg = struct(); end cfg = fill_cfg_defaults(cfg, phy); if nargin < 2 || isempty(jsonFile) if isfield(cfg, 'jsonFile') jsonFile = fullfile(pwd, cfg.jsonFile); else jsonFile = fullfile(pwd, 'sbc3d.json'); end end else cfg = fill_cfg_defaults(cfg, phy); end outFaces = phy.out(:)'; incFaces = phy.inc(:)'; sbcIndex = unique([outFaces, incFaces], 'stable'); nSbc = numel(sbcIndex); sbcType = zeros(1, nSbc); for k = 1:nSbc if ismember(sbcIndex(k), incFaces) sbcType(k) = 1; else sbcType(k) = 0; end end [nbrBoundary, boundaryFlag] = infer_boundary_flag(phy, cfg); nDom = numel(phy.eps); doc = struct(); doc.FemType = cfg.FemType; doc.EletricType = cfg.EletricType; doc.lambda = phy.lda0; doc.NbrBoundary = nbrBoundary; doc.BoundaryFlag = boundaryFlag; if isfield(phy, 'src') && isfield(phy, 'dst') && ~isempty(phy.src) if isfield(cfg, 'pbcPhi') pbcPhi = cfg.pbcPhi; else pbcPhi = exp(1i * 2 * pi / 6); end if isfield(cfg, 'pbcAngle') pbcAngle = cfg.pbcAngle; else pbcAngle = pi / 3; end doc.pbc = struct( ... 'srcIndex', phy.src(:)', ... 'dstIndex', phy.dst(:)', ... 'phiR', real(pbcPhi(:)'), ... 'phiI', imag(pbcPhi(:)'), ... 'pbcAngle', pbcAngle); end doc.sbc = struct( ... 'Index', sbcIndex, ... 'SBCType', sbcType, ... 'E0x', {repmat({num2str(phy.Einc(1))}, 1, nSbc)}, ... 'E0y', {repmat({num2str(phy.Einc(2))}, 1, nSbc)}, ... 'E0z', {repmat({num2str(phy.Einc(3))}, 1, nSbc)}, ... 'kx', zeros(1, nSbc), ... 'ky', zeros(1, nSbc), ... 'kz', zeros(1, nSbc)); doc.NbrDomain = nDom; doc.domainType = repmat(2, 1, nDom); doc.domainIndex = 0:(nDom - 1); doc.matType = zeros(1, nDom); doc.epsilonrR = phy.eps(:)'; doc.epsilonrI = zeros(1, nDom); doc.murR = phy.mur(:)'; doc.murI = zeros(1, nDom); doc.chiheR = zeros(1, nDom); doc.chiehR = zeros(1, nDom); doc.chiheI = zeros(1, nDom); doc.chiehI = zeros(1, nDom); doc.sigma = zeros(1, nDom); doc.n = ones(1, nDom); doc.k = zeros(1, nDom); doc.MeshFile = cfg.MeshFile; doc.OutFile = cfg.OutFile; txt = pretty_json(doc); fid = fopen(jsonFile, 'w'); if fid < 0 error('无法写入: %s', jsonFile); end cleanup = onCleanup(@() fclose(fid)); fprintf(fid, '%s', txt); fprintf('已写出 %s (%d 字节, lambda=%.4g, NbrBoundary=%d)\n', ... jsonFile, length(txt), phy.lda0, doc.NbrBoundary); end function cfg = fill_cfg_defaults(cfg, phy) if nargin < 2, phy = struct(); end if ~isfield(cfg, 'FemType'), cfg.FemType = 4; end if ~isfield(cfg, 'EletricType'), cfg.EletricType = 2; end if ~isfield(cfg, 'MeshFile'), cfg.MeshFile = 'SBCmesh.dat'; end if ~isfield(cfg, 'OutFile'), cfg.OutFile = './OutFile'; end if ~isfield(cfg, 'NbrBoundary') || ~isfield(cfg, 'BoundaryFlag') [cfg.NbrBoundary, cfg.BoundaryFlag] = infer_boundary_flag(phy, cfg); end end function [nbrBoundary, boundaryFlag] = infer_boundary_flag(phy, cfg) % Prefer explicit cfg; else build from phy face domains (PBC case 1..9). if isfield(cfg, 'NbrBoundary') && isfield(cfg, 'BoundaryFlag') ... && numel(cfg.BoundaryFlag) == cfg.NbrBoundary nbrBoundary = cfg.NbrBoundary; boundaryFlag = cfg.BoundaryFlag; return; end if isfield(phy, 'src') && isfield(phy, 'dst') && ~isempty(phy.src) allDom = [phy.src(:); phy.dst(:); phy.out(:); phy.inc(:)]; nbrBoundary = max(allDom); boundaryFlag = zeros(1, nbrBoundary); for d = 1:nbrBoundary if ismember(d, [phy.src(:); phy.dst(:)]) boundaryFlag(d) = 4; elseif ismember(d, [phy.out(:); phy.inc(:)]) boundaryFlag(d) = 2; else boundaryFlag(d) = 0; end end return; end % Legacy SBC full model (14 COMSOL boundaries) nbrBoundary = 14; boundaryFlag = [0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2]; end function txt = pretty_json(doc) lines = cell(1, 50); n = 0; n = n + 1; lines{n} = '{'; n = n + 1; lines{n} = sprintf(' "FemType": %d,', doc.FemType); n = n + 1; lines{n} = sprintf(' "EletricType": %d,', doc.EletricType); n = n + 1; lines{n} = sprintf(' "lambda": %s,', json_num(doc.lambda)); n = n + 1; lines{n} = sprintf(' "NbrBoundary": %d,', doc.NbrBoundary); n = n + 1; lines{n} = sprintf(' "BoundaryFlag": %s,', json_int_array(doc.BoundaryFlag)); if isfield(doc, 'pbc') n = n + 1; lines{n} = ' "pbc": {'; n = n + 1; lines{n} = sprintf(' "srcIndex": %s,', json_int_array(doc.pbc.srcIndex)); n = n + 1; lines{n} = sprintf(' "dstIndex": %s,', json_int_array(doc.pbc.dstIndex)); n = n + 1; lines{n} = sprintf(' "phiR": %s,', json_float_array(doc.pbc.phiR)); n = n + 1; lines{n} = sprintf(' "phiI": %s,', json_float_array(doc.pbc.phiI)); n = n + 1; lines{n} = sprintf(' "pbcAngle": %s', json_num(doc.pbc.pbcAngle)); n = n + 1; lines{n} = ' },'; end n = n + 1; lines{n} = ' "sbc": {'; n = n + 1; lines{n} = sprintf(' "Index": %s,', json_int_array(doc.sbc.Index)); n = n + 1; lines{n} = sprintf(' "SBCType": %s,', json_int_array(doc.sbc.SBCType)); n = n + 1; lines{n} = sprintf(' "E0x": %s,', json_str_array(doc.sbc.E0x)); n = n + 1; lines{n} = sprintf(' "E0y": %s,', json_str_array(doc.sbc.E0y)); n = n + 1; lines{n} = sprintf(' "E0z": %s,', json_str_array(doc.sbc.E0z)); n = n + 1; lines{n} = sprintf(' "kx": %s,', json_int_array(doc.sbc.kx)); n = n + 1; lines{n} = sprintf(' "ky": %s,', json_int_array(doc.sbc.ky)); n = n + 1; lines{n} = sprintf(' "kz": %s', json_int_array(doc.sbc.kz)); n = n + 1; lines{n} = ' },'; n = n + 1; lines{n} = sprintf(' "NbrDomain": %d,', doc.NbrDomain); n = n + 1; lines{n} = sprintf(' "domainType": %s,', json_int_array(doc.domainType)); n = n + 1; lines{n} = sprintf(' "domainIndex": %s,', json_int_array(doc.domainIndex)); n = n + 1; lines{n} = sprintf(' "matType": %s,', json_int_array(doc.matType)); n = n + 1; lines{n} = sprintf(' "epsilonrR": %s,', json_float_array(doc.epsilonrR)); n = n + 1; lines{n} = sprintf(' "epsilonrI": %s,', json_float_array(doc.epsilonrI)); n = n + 1; lines{n} = sprintf(' "murR": %s,', json_float_array(doc.murR)); n = n + 1; lines{n} = sprintf(' "murI": %s,', json_float_array(doc.murI)); n = n + 1; lines{n} = sprintf(' "chiheR": %s,', json_float_array(doc.chiheR)); n = n + 1; lines{n} = sprintf(' "chiehR": %s,', json_float_array(doc.chiehR)); n = n + 1; lines{n} = sprintf(' "chiheI": %s,', json_float_array(doc.chiheI)); n = n + 1; lines{n} = sprintf(' "chiehI": %s,', json_float_array(doc.chiehI)); n = n + 1; lines{n} = sprintf(' "sigma": %s,', json_float_array(doc.sigma)); n = n + 1; lines{n} = sprintf(' "n": %s,', json_float_array(doc.n)); n = n + 1; lines{n} = sprintf(' "k": %s,', json_float_array(doc.k)); n = n + 1; lines{n} = sprintf(' "MeshFile": "%s",', doc.MeshFile); n = n + 1; lines{n} = sprintf(' "OutFile": "%s"', doc.OutFile); n = n + 1; lines{n} = '}'; lines = lines(1:n); txt = join_lines(lines); end function s = join_lines(lines) if isempty(lines) s = ''; return; end s = lines{1}; for i = 2:numel(lines) s = [s, sprintf('\n'), lines{i}]; %#ok end end function s = json_num(x) s = num2str(x, '%.16g'); end function s = json_int_array(v) v = v(:)'; parts = cell(1, numel(v)); for i = 1:numel(v) parts{i} = sprintf('%d', round(v(i))); end s = sprintf('[%s]', join_csv(parts)); end function s = json_float_array(v) v = v(:)'; parts = cell(1, numel(v)); for i = 1:numel(v) parts{i} = num2str(v(i), '%.16g'); end s = sprintf('[%s]', join_csv(parts)); end function s = json_str_array(c) parts = cell(1, numel(c)); for i = 1:numel(c) parts{i} = sprintf('"%s"', char(c{i})); end s = sprintf('[%s]', join_csv(parts)); end function s = join_csv(parts) if isempty(parts) s = ''; elseif numel(parts) == 1 s = parts{1}; else s = parts{1}; for i = 2:numel(parts) s = [s, ', ', parts{i}]; %#ok end end end