From dc5d2cde0b435cfb60c41da5458e244788197997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ne=C4=8Dada?= Date: Sat, 14 Dec 2019 13:26:40 +0200 Subject: [PATCH] Upgrades to argproc.py, finite rectangular lattice scatter script. Former-commit-id: 36aba53dc445752cf50e1638883f5a280ccab753 --- ...at-scatter.py => finiterectlat-scatter.py} | 21 +++----- qpms/argproc.py | 51 +++++++++++++++++-- 2 files changed, 54 insertions(+), 18 deletions(-) rename misc/{finitesqlat-scatter.py => finiterectlat-scatter.py} (87%) diff --git a/misc/finitesqlat-scatter.py b/misc/finiterectlat-scatter.py similarity index 87% rename from misc/finitesqlat-scatter.py rename to misc/finiterectlat-scatter.py index e85a129..5ee8c57 100755 --- a/misc/finitesqlat-scatter.py +++ b/misc/finiterectlat-scatter.py @@ -4,13 +4,9 @@ import math from qpms.argproc import ArgParser -ap = ArgParser(['single_particle', 'single_omega', 'single_lMax']) -ap.add_argument("-p", "--period", type=float, required=True, help='square lattice period') -ap.add_argument("--Nx", type=int, required=True, help='Array size x') -ap.add_argument("--Ny", type=int, required=True, help='Array size y') +ap = ArgParser(['rectlattice2d_finite', 'single_particle', 'single_lMax', 'single_omega']) ap.add_argument("-k", '--kx-lim', nargs=2, type=float, required=True, help='k vector', metavar=('KX_MIN', 'KX_MAX')) # ap.add_argument("--kpi", action='store_true', help="Indicates that the k vector is given in natural units instead of SI, i.e. the arguments given by -k shall be automatically multiplied by pi / period (given by -p argument)") -ap.add_argument("--rank-tol", type=float, required=False) ap.add_argument("-o", "--output", type=str, required=False, help='output path (if not provided, will be generated automatically)') ap.add_argument("-N", type=int, default="151", help="Number of angles") ap.add_argument("-O", "--plot-out", type=str, required=False, help="path to plot output (optional)") @@ -23,12 +19,14 @@ a=ap.parse_args() import logging logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO) +Nx, Ny = a.size +px, py = a.period particlestr = ("sph" if a.height is None else "cyl") + ("_r%gnm" % (a.radius*1e9)) if a.height is not None: particlestr += "_h%gnm" % (a.height * 1e9) -defaultprefix = "%s_p%gnm_%dx%d_m%s_n%g_angles(%g_%g)_Ey_f%geV_L%d_cn%d" % ( - particlestr, a.period*1e9, a.Nx, a.Ny, str(a.material), a.refractive_index, a.kx_lim[0], a.kx_lim[1], a.eV, a.lMax, a.N) -logging.info("Dafault file prefix: %s" % defaultprefix) +defaultprefix = "%s_p%gnmx%gnm_%dx%d_m%s_n%g_angles(%g_%g)_Ey_f%geV_L%d_cn%d" % ( + particlestr, px*1e9, py*1e9, Nx, Ny, str(a.material), a.refractive_index, a.kx_lim[0], a.kx_lim[1], a.eV, a.lMax, a.N) +logging.info("Default file prefix: %s" % defaultprefix) import numpy as np @@ -44,12 +42,9 @@ eh = eV/hbar dbgmsg_enable(DebugFlags.INTEGRATION) -px=a.period -py=a.period - #Particle positions -orig_x = (np.arange(a.Nx/2) + (0 if (a.Nx % 2) else .5)) * px -orig_y = (np.arange(a.Ny/2) + (0 if (a.Ny % 2) else .5)) * py +orig_x = (np.arange(Nx/2) + (0 if (Nx % 2) else .5)) * px +orig_y = (np.arange(Ny/2) + (0 if (Ny % 2) else .5)) * py orig_xy = np.stack(np.meshgrid(orig_x, orig_y), axis = -1) diff --git a/qpms/argproc.py b/qpms/argproc.py index eaacd2b..ba0363f 100644 --- a/qpms/argproc.py +++ b/qpms/argproc.py @@ -3,13 +3,28 @@ Common snippets for argument processing in command line scripts; legacy scripts ''' import argparse +import sys + +def make_action_sharedlist(opname, listname): + class opAction(argparse.Action): + def __call__(self, parser, args, values, option_string=None): + if (not hasattr(args, listname)) or getattr(args, listname) is None: + setattr(args, listname, list()) + getattr(args, listname).append((opname, values)) + return opAction + +class AppendTupleAction(argparse.Action): + ''' A variation on the 'append' builtin action from argparse, but uses tuples for the internal groupings instead of lists ''' + def __call__(self, parser, args, values, option_string=None): + if (not hasattr(args, self.dest)) or getattr(args, self.dest) is None: + setattr(args, self.dest, list()) + getattr(args, self.dest).append(tuple(values)) class ArgParser: ''' Common argument parsing engine for QPMS python CLI scripts. ''' atomic_arguments = { - 'sqlat_period': lambda ap: ap.add_argument("-p", "--period", type=float, required=True, help='square lattice period'), - 'rectlat_Nx': lambda ap: ap.add_argument("--Nx", type=int, required=True, help='array size x'), - 'rectlat_Ny': lambda ap: ap.add_argument("--Ny", type=int, required=True, help='array size y'), + 'rectlattice2d_periods': lambda ap: ap.add_argument("-p", "--period", type=float, nargs='+', required=True, help='square/rectangular lattice periods', metavar=('px','[py]')), + 'rectlattice2d_counts': lambda ap: ap.add_argument("--size", type=int, nargs=2, required=True, help='rectangular array size (particle column, row count)', metavar=('NCOLS', 'NROWS')), 'single_frequency_eV': lambda ap: ap.add_argument("-f", "--eV", type=float, required=True, help='radiation angular frequency in eV'), 'single_material': lambda ap: ap.add_argument("-m", "--material", help='particle material (Au, Ag, ... for Lorentz-Drude or number for constant refractive index)', default='Au', required=True), 'single_radius': lambda ap: ap.add_argument("-r", "--radius", type=float, required=True, help='particle radius (sphere or cylinder)'), @@ -19,9 +34,10 @@ class ArgParser: 'bg_refractive_index': lambda ap: ap.add_argument("-n", "--refractive-index", type=float, default=1.52, help='background medium refractive index'), 'single_lMax': lambda ap: ap.add_argument("-L", "--lMax", type=int, required=True, default=3, help='multipole degree cutoff'), 'single_lMax_extend': lambda ap: ap.add_argument("--lMax-extend", type=int, required=False, default=6, help='multipole degree cutoff for T-matrix calculation (cylindrical particles only'), - 'outfile': lambda ap: ap.add_argument("-o", "--output", type=str, required=False, help='output path (if not provided, will be generated automatically)'), + 'outfile': lambda ap: ap.add_argument("-o", "--output", type=str, required=False, help='output path (if not provided, will be generated automatically)'), # TODO consider type=argparse.FileType('w') 'plot_out': lambda ap: ap.add_argument("-O", "--plot-out", type=str, required=False, help="path to plot output (optional)"), 'plot_do': lambda ap: ap.add_argument("-P", "--plot", action='store_true', help="if -p not given, plot to a default path"), + 'lattice2d_basis': lambda ap: ap.add_argument("-b", "--basis-vector", action=AppendTupleAction, help="basis vector in xy-cartesian coordinates (two required)", dest='basis_vectors', metavar=('X', 'Y')), } feature_sets_available = { # name : (description, dependencies, atoms not in other dependencies, methods called after parsing) @@ -29,6 +45,9 @@ class ArgParser: 'single_particle': ("Single particle definition (shape [currently spherical or cylindrical]) and materials, incl. background)", ('background',), ('single_material', 'single_radius', 'single_height', 'single_lMax_extend'), ('_eval_single_tmgen',)), 'single_lMax': ("Single particle lMax definition", (), ('single_lMax',), ()), 'single_omega': ("Single angular frequency", (), ('single_frequency_eV',), ('_eval_single_omega',)), + 'lattice2d': ("Specification of a generic 2d lattice (spanned by the x,y axes)", (), ('lattice2d_basis',), ('_eval_lattice2d',)), + 'rectlattice2d': ("Specification of a rectangular 2d lattice; conflicts with lattice2d", (), ('rectlattice2d_periods',), ('_eval_rectlattice2d',)), + 'rectlattice2d_finite': ("Specification of a rectangular 2d lattice; conflicts with lattice2d", ('rectlattice2d',), ('rectlattice2d_counts',), ()), } @@ -43,7 +62,7 @@ class ArgParser: def add_feature(self, feat): if feat not in self.features_enabled: if feat not in ArgParser.feature_sets_available: - raise ValueError("Unknown ArgParser feature: %s", feat) + raise ValueError("Unknown ArgParser feature: %s" % feat) #resolve dependencies _, deps, atoms, atparse = ArgParser.feature_sets_available[feat] for dep in deps: @@ -99,4 +118,26 @@ class ArgParser: from .constants import eV, hbar self.omega = self.args.eV * eV / hbar + def _eval_lattice2d(self): # feature: lattice2d + l = len(self.args.basis_vectors) + if l != 2: raise ValueError('Two basis vectors must be specified (have %d)' % l) + from .qpms_c import lll_reduce + self.direct_basis = lll_reduce(self.args.basis_vector, delta=1.) + # import numpy as np + # self.reciprocal_basis1 = np.linalg.inv(self.direct_basis) + # self.reciprocal_basis2pi = 2 * np.pi * self.reciprocal_basis1 + + def _eval_rectlattice2d(self): # feature: rectlattice2d + a = self.args + l = len(a.period) + if (l == 1): # square lattice + a.period = (a.period[0], a.period[0]) + else: + a.period = (a.period[0], a.period[1]) + if (l > 2): + raise ValueError("At most two lattice periods allowed for a rectangular lattice (got %d)" % l) + + import numpy as np + a.basis_vectors = [(a.period[0], 0.), (0., a.period[1])] + self.direct_basis = np.array(a.basis_vectors)