Source code for raytraverse.integrator.integrator

# -*- coding: utf-8 -*-
# Copyright (c) 2020 Stephen Wasilewski, HSLU and EPFL
# =======================================================================
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# =======================================================================
import os
import sys
import tempfile

import numpy as np

from raytraverse import translate
from raytraverse.evaluate import MetricSet
from raytraverse.lightfield import SunsPlaneKD, ZonalLightResult
from raytraverse.lightfield import ResultAxis, LightResult
import raytraverse.integrator.helpers as intg
from raytraverse.mapper import ViewMapper
from raytraverse.utility import pool_call


[docs]class Integrator(object): """collection of lightplanes with KDtree structure for sun position query Parameters ---------- lightplanes: Sequence[raytraverse.lightfield.LightPlaneKD] """ def __init__(self, *lightplanes, includesky=True, includesun=True, sunviewengine=None, ds=False, dv=False): self.lightplanes = lightplanes self._issunplane = [i for i, lp in enumerate(self.lightplanes) if isinstance(lp, SunsPlaneKD)] self.integration_method = "default" # setup special integrators if ds: self.integration_method = "ds" if len(lightplanes) != 3: raise ValueError("ds=True requires 3 lightplanes: sky, " "directsky, directsun") if len(self._issunplane) != 1 or self._issunplane[0] != 2: raise ValueError("ds=True requires 3rd argument is sunplane") self.evaluate_pt = intg.evaluate_pt_ds self. img_pt = intg.img_pt_ds elif dv: self.integration_method = "dv" if len(lightplanes) != 2: raise ValueError("dv=True requires 2 lightplanes: sky, " "directsky") if sunviewengine is None: raise ValueError("dv=True requires a sunviewengine") if len(self._issunplane) > 0: raise ValueError("dv=True is not compatible it sunplane") self.evaluate_pt = intg.evaluate_pt_dv self.img_pt = intg.img_pt_dv else: self.evaluate_pt = intg.evaluate_pt self.img_pt = intg.img_pt self.scene = lightplanes[0].scene self.includesky = includesky self.includesun = includesun self._sunviewengine = sunviewengine
[docs] def make_images(self, skydata, points, vm, viewangle=180., res=512, interp=False, prefix="img", namebyindex=False, suntol=10.0, blursun=False, resamprad=0.0, bandwidth=10): """see namebyindex for file naming conventions Parameters ---------- skydata: raytraverse.sky.Skydata points: np.array shape (N, 3) vm: Union[raytraverse.mapper.ViewMapper, np.array] either a predefined ViewMapper (used for all points) or an array of view directions (will use a 180 degree view angle when initializing ViewMapper) viewangle: float, optional view opening for sensor (0-180,360) when vm is given as an array of view directions. res: int, optional image resolution interp: bool, optional interpolate image prefix: str, optional prefix for output file naming namebyindex: bool, optional if False (default), names images by: <prefix>_sky-<row>_pt-<x>_<y>_<z>_vd-<dx>_<dy>_<dz>.hdr if True, names images by: <prefix>_sky-<row>_pt-<pidx>_vd-<vidx>.hdr, where pidx, vidx are refer to the order of points, and vm. Returns ------- np.array of out_files shape (skies, points, views) """ points = np.atleast_2d(points) if hasattr(vm, "dxyz"): vms = [vm] else: dxyz = np.asarray(vm).reshape(-1, 3) vms = [ViewMapper(d, viewangle) for d in dxyz] oshape = (len(skydata.maskindices), len(points), len(vms)) idxs, skydatas, dsns, vecs = self._group_query(skydata, points) self.scene.log(self, f"Making Images for {oshape[2]}" f" view directions at {oshape[1]} points under " f"{oshape[0]} skies", True) skylabs = [f"{skydata.srcname}-{si:04d}" for si in skydata.maskindices] if namebyindex: ptlabs = [f"pt-{i:03d}" for i in range(len(points))] viewlabs = [f"vd-{i:03d}" for i in range(len(vms))] else: ptlabs = ["pt-{:.1f}_{:.1f}_{:.1f}".format(*pt) for pt in points] viewlabs = ["vd-{:.1f}_{:.1f}_{:.1f}".format(*v.dxyz) for v in vms] a, b, c = np.meshgrid(skylabs, ptlabs, viewlabs, indexing='ij') combos = np.stack((a.ravel().astype(object), b.ravel(), c.ravel())) combos = combos.T.reshape(-1, len(vms), 3) # use parallel processing skinfo = skydata.skydata[skydata.fullmask] s = (*oshape[0:2], skinfo.shape[1]) skinfo = np.broadcast_to(skinfo[:, None, :], s).reshape(-1, s[-1]) mask_kwargs = dict(combos=combos, qpts=vecs[:, 3:], skinfo=skinfo) outfs, _ = self._process_mgr(idxs, skydatas, dsns, self.img_pt, message="Making Images", mask_kwargs=mask_kwargs, vms=vms, res=res, interp=interp, prefix=prefix, suntol=suntol, blursun=blursun, resamprad=resamprad, bandwidth=bandwidth) return sorted([i for j in outfs for i in j])
def _echunk(self, evalfunc, esize, csize, skydata, points, vm, **kwargs): fd, rbase = tempfile.mkstemp(dir=f"./", prefix='tmp_eval') rbase = rbase.rsplit("/", 1)[-1] with os.fdopen(fd) as f: pass os.remove(rbase) d = np.linspace(0, len(skydata.maskindices), int(esize/csize) + 2).astype(int) self.scene.log(self, f"breaking evaluation into {len(d) - 1} chunks," f" writing temporary results to {rbase}_XX.npz" , True) slices = [slice(d[i], d[i + 1], 1) for i in range(len(d) - 1)] omask = np.copy(skydata.mask) for i, slc in enumerate(slices): self.scene.log(self, f"evaluating part {i + 1} of {len(slices)}", True) skydata.mask = skydata.maskindices[slc] lr = evalfunc(skydata, points, vm, emax=-1, **kwargs) skydata.mask = omask lr.write(f"{rbase}_{i:02d}.npz") idx = np.arange(len(slices)) lrs = [type(lr)(f"{rbase}_{i:02d}.npz") for i in idx] lr = lrs[0].merge(*lrs[1:]) [os.remove(f"{rbase}_{i:02d}.npz") for i in idx] return lr
[docs] def evaluate(self, skydata, points, vm, viewangle=180., metricclass=MetricSet, metrics=None, datainfo=False, srconly=False, suntol=10.0, blursun=False, coercesumsafe=False, stol=10, minsun=1, emax=10000, **kwargs): """apply sky data and view queries to daylightplane to return metrics parallelizes and optimizes run order. Parameters ---------- skydata: raytraverse.sky.Skydata points: np.array shape (N, 3) vm: Union[raytraverse.mapper.ViewMapper, np.array] either a predefined ViewMapper (used for all points) or an array of view directions (will use 'viewangle' when initializing ViewMapper) viewangle: float, optional view opening for sensor (0-180,360) when vm is given as an array of view directions, note that for illuminance based metrics, a value of 360 may not make sense as values behind will be negative. metricclass: raytraverse.evaluate.BaseMetricSet, optional metrics: Sized, optional srconly: bool, optional sun only calculations suntol: float, optional if Integrator has an engine, resample sun views when actual sun position error is greater than this many degrees. blursun: bool, optional apply human PSF to small bright sources coercesumsafe: bool, optional attempt to calculate sumsafe metrics datainfo: Union[Sized[str], bool], optional include information about source data as additional metrics. Valid values include: ["pt_err", "pt_idx", "src_err", "src_idx"]. If True, includes all. stol: Union[float, int], optional maximum angle (in degrees) for matching sun vectors (zonal) minsun: int, optional if atleast these many suns are not returned based on stol, directly query for this number of results (regardless of sun error) (zonal) Returns ------- raytraverse.lightfield.LightResult """ # delegate to zonal if (points is None and len(self._issunplane) > 0 and np.any(skydata.sun[:, 3] > 0)): return self.zonal_evaluate(skydata, self.lightplanes[0].pm, vm, viewangle=viewangle, metricclass=metricclass, metrics=metrics, datainfo=datainfo, srconly=srconly, suntol=suntol, blursun=blursun, coercesumsafe=coercesumsafe, stol=stol, minsun=minsun, emax=emax, **kwargs) if points is None: points, skarea = self._get_fixed_points(self.lightplanes[0].pm) else: points = np.atleast_2d(points) if emax > 0: esize = len(skydata.maskindices) * len(points) else: esize = 0 if 0 < emax < esize: lr = self._echunk(self.evaluate, esize, emax, skydata, points, vm, viewangle=viewangle, metricclass=metricclass, metrics=metrics, datainfo=datainfo, srconly=srconly, suntol=suntol, blursun=blursun, coercesumsafe=coercesumsafe, stol=stol, minsun=minsun, **kwargs) else: (vm, vms, cmetrics, ometrics, sumsafe, needs_post) = self._check_params(vm, viewangle, metrics, metricclass, coercesumsafe) tidxs, skydatas, dsns, vecs = self._group_query(skydata, points) oshape = (len(skydata.maskindices), len(points), len(vms), len(cmetrics)) self.scene.log(self, f"Evaluating {len(ometrics)} metrics for " f"{oshape[2]} view directions at {oshape[1]} " f"points under {oshape[0]} skies", True) fields, isort = self._process_mgr(tidxs, skydatas, dsns, self.evaluate_pt, message="Evaluating Points", srconly=srconly, sumsafe=sumsafe, metricclass=metricclass, metrics=cmetrics, vm=vm, vms=vms, suntol=suntol, blursun=blursun, **kwargs) # sort back to original order and reshape fields = np.concatenate(fields, axis=0)[isort].reshape(oshape) if needs_post: fields = self._post_process_metrics(fields, ometrics, cmetrics, **kwargs) sinfo, dinfo = self._sinfo(datainfo, vecs, tidxs, oshape[0:2]) if sinfo is not None: nshape = list(sinfo.shape) nshape[2] = fields.shape[2] sinfo = np.broadcast_to(sinfo, nshape) fields = np.concatenate((fields, sinfo), axis=-1) # compose axes: (skyaxis, ptaxis, viewaxis, metricaxis) axes = (ResultAxis(skydata.rowlabel[skydata.fullmask], f"sky"), ResultAxis(points, "point"), ResultAxis([v.dxyz for v in vms], "view"), ResultAxis(list(ometrics) + dinfo, "metric")) lr = LightResult(fields, *axes, boundary=self.lightplanes[0].pm) return lr
[docs] def zonal_evaluate(self, skydata, pm, vm, viewangle=180., metricclass=MetricSet, metrics=None, srconly=False, suntol=10.0, blursun=False, coercesumsafe=False, stol=10, minsun=1, datainfo=False, calcarea=True, emax=10000, **kwargs): """apply sky data and view queries to daylightplane to return metrics parallelizes and optimizes run order. Parameters ---------- see evaluate Returns ------- raytraverse.lightfield.ZonalLightResult """ # delegate back to evaluate if len(self._issunplane) == 0 or np.all(skydata.sun[:, 3] == 0): return self.evaluate(skydata, None, vm, viewangle=viewangle, metricclass=metricclass, metrics=metrics, datainfo=datainfo, srconly=srconly, suntol=suntol, blursun=blursun, coercesumsafe=coercesumsafe, stol=stol, minsun=minsun, emax=emax, **kwargs) # only chunk on first call if emax > 0: # call this first just to measure size of evaluation, skip calcarea # for now (sunmask, suns, pts, sundata, skarea) = self._zonal_group_query(skydata, pm, stol=stol, minsun=minsun, calcarea=False) sunpts = np.sum([len(p) for p in sundata[0]]) skpts = len(pts) * (len(skydata.smtx) - len(sundata[0])) esize = sunpts + skpts else: esize = 0 if 0 < emax < esize: lr = self._echunk(self.zonal_evaluate, esize, emax, skydata, pm, vm, viewangle=viewangle, metricclass=metricclass, metrics=metrics, datainfo=datainfo, srconly=srconly, suntol=suntol, blursun=blursun, coercesumsafe=coercesumsafe, stol=stol, minsun=minsun, **kwargs) else: (sunmask, suns, pts, sundata, skarea) = self._zonal_group_query(skydata, pm, stol=stol, minsun=minsun, calcarea=calcarea) (vm, vms, cmetrics, ometrics, sumsafe, needs_post) = self._check_params(vm, viewangle, metrics, metricclass, coercesumsafe) result = self._unmask_data(skydata.smtx, sunmask, suns, pts, sundata, skarea) smtx, dsns, all_vecs, sunidx, serr, areas, cnts = result tidxs, skydatas = self._match_ragged(smtx, dsns, sunidx, all_vecs) self.scene.log(self, f"Evaluating {len(ometrics)} metrics for " f"{len(vms)} view directions across zone " f"'{pm.name}' under {len(skydata.sun)} skies", True) fields, isort = self._process_mgr(tidxs, skydatas, dsns, self.evaluate_pt, message="Evaluating Points", srconly=srconly, sumsafe=sumsafe, metricclass=metricclass, metrics=cmetrics, vm=vm, vms=vms, suntol=suntol, blursun=blursun, **kwargs) fields = np.concatenate(fields, axis=0)[isort] if needs_post: fields = self._post_process_metrics(fields, ometrics, cmetrics, **kwargs) oshape = (len(fields), len(vms)) pmetrics = ['x', 'y', 'z', 'area'] if datainfo: sinfo, dinfo = self._zonal_sinfo(serr, tidxs, oshape + (2,)) if sinfo is not None: fields = np.concatenate((sinfo, fields), axis=-1) pmetrics += dinfo areas = np.broadcast_to(areas[:, None, None], oshape + (1,)) axes = [ResultAxis(skydata.rowlabel[skydata.fullmask], f"sky"), ResultAxis([pm.name], f"zone"), ResultAxis([v.dxyz for v in vms], "view"), ResultAxis(pmetrics + ometrics, "metric")] strides = np.cumsum(cnts)[:-1] fvecs = np.broadcast_to(all_vecs[:, None, 3:], oshape + (3,)) fields = np.concatenate((fvecs, areas, fields), axis=-1) fields = np.split(fields, strides) lr = ZonalLightResult(fields, *axes, boundary=pm) return lr
@staticmethod def _post_process_metrics(fields, ometrics, cmetrics, **kwargs): ofields = np.zeros(fields.shape[:-1] + (len(ometrics),)) for i, m in enumerate(ometrics): if m in cmetrics: ofields[..., i] = fields[..., cmetrics.index(m)] elif m == "dgp": illum = fields[..., cmetrics.index("illum")] pwsl2 = fields[..., cmetrics.index("pwsl2")] t1 = 5.87*10**-5*illum t2 = 9.18*10**-2*np.log10(1 + pwsl2/np.power(illum, 1.87)) ll = 1 if "lowlight" in kwargs and kwargs["lowlight"]: ll = np.where(illum < 500, np.exp(0.024*illum - 4)/ (1 + np.exp(0.024*illum - 4)), 1) ofields[..., i] = np.minimum(ll*(t1 + t2 + 0.16), 1.0) elif m == "ugp": pwsl2 = fields[..., cmetrics.index("pwsl2")] backlum = fields[..., cmetrics.index("backlum")] with np.errstate(divide='ignore'): ugr = np.maximum(0, 8*np.log10(0.25*pwsl2/backlum)) ofields[..., i] = (1 + 2/7*10**(-(ugr + 5)/40))**-10 return ofields def _get_fixed_points(self, pm, ptfilter=0.01): pts = [lp.vecs for lp in self.lightplanes if not isinstance(lp, SunsPlaneKD)] if len(pts) > 0: pts = np.vstack(pts) pts = pts[pm.in_view(pts, False)] pts = pts[translate.cull_vectors(pts, ptfilter)] skarea = translate.calc_omega(pts, pm) else: pts = pm.dxyz.reshape(1, 3) skarea = np.array([pm.area]) return pts, skarea @staticmethod def _check_params(vm, viewangle=180., metrics=None, metricclass=MetricSet, coercesumsafe=False): if hasattr(vm, "dxyz"): vms = [vm] else: dxyz = np.asarray(vm).reshape(-1, 3) vm = ViewMapper() vms = [ViewMapper(d, viewangle) for d in dxyz] if metrics is None: metrics = metricclass.defaultmetrics needs_post = False if coercesumsafe: ometrics = [] cmetrics = set() s2s = True for m in metrics: if m in metricclass.safe2sum: cmetrics.add(m) ometrics.append(m) elif m == "dgp": cmetrics.update(["illum", "pwsl2"]) ometrics.append(m) needs_post = True elif m == "ugp": cmetrics.update(["backlum", "pwsl2"]) ometrics.append(m) needs_post = True else: print(f"could not coerce metric {m} to sumsafe", file=sys.stderr) ometrics = metrics cmetrics = metrics s2s = metricclass.check_safe2sum(metrics) break cmetrics = list(cmetrics) else: ometrics = metrics cmetrics = metrics s2s = metricclass.check_safe2sum(metrics) return vm, vms, cmetrics, ometrics, s2s, needs_post def _group_query(self, skydata, points): # query and group sun positions gshape = (len(skydata.maskindices), len(points)) suns = skydata.sun[:, None, 0:3] pts = np.atleast_2d(points)[None, :] vecs = np.concatenate(np.broadcast_arrays(suns, pts), -1).reshape(-1, 6) skydatas, dsns = self._unroll_sky_grid(skydata, gshape) idxs = [] for lp in self.lightplanes: if lp.vecs.shape[1] == 6: idxs.append(lp.query(vecs)[0]) else: idx, err = lp.query(points) idxs.append(np.broadcast_to(idx, gshape).ravel()) if self.integration_method == "dv": idxs.append(np.broadcast_to(skydata.sunproxy[:, None], gshape).ravel()) return np.stack(idxs), skydatas, dsns, vecs def _zonal_group_query(self, skydata, pm, stol=10, minsun=1, calcarea=True): pts, skarea = self._get_fixed_points(pm) # only look for suns when relevant sunmask = skydata.sun[:, 3] > 0 suns = skydata.sun sunplane = self.lightplanes[self._issunplane[0]] # for each sun, matching vectors, indices, and solar errors vecs, idx, ds = sunplane.query_by_suns(suns[sunmask, 0:3], fixed_points=pts, stol=stol, minsun=minsun) spts = [v[:, 3:] for v in vecs] if calcarea: areas = pool_call(translate.calc_omega, spts, pm, expandarg=False, desc="calculating areas", pbar=self.scene.dolog) else: areas = [[0]*len(v) for v in spts] sundata = (vecs, idx, ds, areas) return sunmask, suns, pts, sundata, skarea def _sinfo(self, datainfo, vecs, idxs, oshape): """error and bin information for evaluate queries""" dinfo = ["src_err", "src_idx", "pt_err", "pt_idx"] if not datainfo: dinfo = [] elif hasattr(datainfo, "__len__"): dinfo = [i for i in dinfo if i in datainfo] if len(dinfo) == 0: return None, dinfo dinfo2 = [] sinfo = [] for lp, idx in zip(self.lightplanes, idxs): for di in dinfo: if di == "src_err" and lp.vecs.shape[1] == 6: snerr = np.linalg.norm(vecs[:, :3] - lp.vecs[idx, :3], axis=-1) sinfo.append(translate.chord2theta(snerr)*(180/np.pi)) dinfo2.append(f"{lp.src}_{di}") elif di == "src_idx" and lp.vecs.shape[1] == 6: srcidx = lp.data.idx[idx, 0] sinfo.append(srcidx) dinfo2.append(f"{lp.src}_{di}") elif di == "pt_err": pterr = np.linalg.norm(vecs[:, 3:] - lp.vecs[idx, -3:], axis=-1) sinfo.append(pterr) dinfo2.append(f"{lp.src}_{di}") elif di == "pt_idx": try: ptidx = lp.data.idx[idx, 1] except (IndexError, AttributeError): ptidx = idx sinfo.append(ptidx) dinfo2.append(f"{lp.src}_{di}") return np.array(sinfo).T.reshape(*oshape, 1, -1), dinfo2 def _zonal_sinfo(self, serr, idxs, oshape): """error and bin information for evaluate queries""" if len(self._issunplane) > 0: dinfo = ["src_err", "src_idx"] lp = self.lightplanes[self._issunplane[0]] idx = idxs[self._issunplane[0]] srcidx = np.full(len(idx), -1) mask = idx >= 0 srcidx[mask] = lp.data.idx[idx[mask], 0] sinfo = np.broadcast_to(np.stack((serr, srcidx)).T[:, None], oshape) else: dinfo = [] sinfo = None return sinfo, dinfo def _match_ragged(self, smtx, dsns, sunidx, all_vecs): if self.integration_method == "ds": skyq = self.lightplanes[0].query(all_vecs[:, 3:])[0] tidxs = np.stack([skyq, skyq, sunidx]) skydatas = [smtx, dsns[:, 4:], dsns[:, 3:4]] elif self.integration_method == "dv": skyq = self.lightplanes[0].query(all_vecs[:, 3:])[0] tidxs = np.stack([skyq, skyq]) skydatas = [smtx, dsns[:, 4:]] else: tidxs = [] skydatas = [] for i, lp in enumerate(self.lightplanes): if i == self._issunplane[0]: tidxs.append(sunidx) skydatas.append(dsns[:, 3:4]) else: tidxs.append(lp.query(all_vecs[:, 3:])[0]) skydatas.append(smtx) tidxs = np.stack(tidxs) return tidxs, skydatas def _unroll_sky_grid(self, skydata, oshape): # broadcast skydata to match full indexing s = (*oshape, skydata.smtx.shape[1]) smtx = np.broadcast_to(skydata.smtx[:, None, :], s).reshape(-1, s[-1]) ds = (*oshape, skydata.sun.shape[1]) dsns = np.broadcast_to(skydata.sun[:, None, :], ds).reshape(-1, ds[-1]) if self.integration_method == "ds": skydatas = [smtx, dsns[:, 4:], dsns[:, 3:4]] return skydatas, dsns if self.integration_method == "dv": skydatas = [np.hstack((smtx, dsns[:, 4:])), dsns[:, 3:4]] return skydatas, dsns skydatas = [] # generic, assumes sun and/or sky, uses patch sun when no sun present skyi = None suni = None for i, lp in enumerate(self.lightplanes): if lp.vecs.shape[1] == 3: skyi = i skydatas.append(smtx) elif self.includesun: suni = i skydatas.append(dsns[:, 3:4]) if self.includesun and suni is None and skyi is not None: dprx = skydata.smtx_patch_sun(includesky=self.includesky) dprx = np.broadcast_to(dprx[:, None, :], s).reshape(-1, s[-1]) skydatas[skyi] = dprx return skydatas, dsns @staticmethod def _unmask_data(dsmtx, sunmask, suns, pts, sundata, skarea): # calculate size of return arrays cnts = np.full(len(sunmask), len(pts)) cnts[sunmask] = [len(s) for s in sundata[0]] tcnt = np.sum(cnts) all_vecs = np.zeros((tcnt, 6)) sunidx = np.full(tcnt, -1, dtype=np.int64) d = np.zeros(tcnt) areas = np.zeros(tcnt) smtx = np.zeros((tcnt, dsmtx.shape[1])) dsns = np.zeros((tcnt, suns.shape[1])) # track suns (one for each True sunmask j = 0 # track return array slices ci = 0 # loop over timesteps and allocate point/sun sky combos for i, sm in enumerate(sunmask): if sm: cnt = len(sundata[0][j]) all_vecs[ci:ci + cnt] = sundata[0][j] sunidx[ci:ci + cnt] = sundata[1][j] d[ci:ci + cnt] = sundata[2][j] areas[ci:ci + cnt] = sundata[3][j] j += 1 else: cnt = len(pts) all_vecs[ci:ci + cnt, 0:3] = suns[i, 0:3] all_vecs[ci:ci + cnt, 3:6] = pts areas[ci:ci + cnt] = skarea smtx[ci:ci + cnt] = dsmtx[i] dsns[ci:ci + cnt] = suns[i] ci += cnt return smtx, dsns, all_vecs, sunidx, d, areas, cnts @staticmethod def _sort_run_data(tidxs): # unique returns sorted values (evaluation order) qtup, qidx = np.unique(tidxs.T, axis=0, return_index=True) tup_sort = np.lexsort(tidxs[::-1]) # make an inverse sort to undo evaluation order tup_isort = np.argsort(tup_sort, kind='stable') return qtup, qidx, tup_isort def _process_mgr(self, tidxs, skydatas, dsns, eval_fn, message="Evaluating Points", mask_kwargs=None, **eval_kwargs): qtup, qidx, tup_isort = self._sort_run_data(tidxs) self.scene.log(self, f"Calculating {len(qidx)} sun/sky/pt combinations", True) if self._sunviewengine is not None: eval_kwargs.update(svengine=self._sunviewengine) lptis = [] for qi, qt in zip(qidx, qtup): lptis.append(([(lp.data, tidx[qi]) for lp, tidx in zip(self.lightplanes, tidxs)], qt)) fields = pool_call(_load_pts, lptis, eval_fn, tidxs.T, mask_kwargs, skydatas, dsns, desc=message, pbar=self.scene.dolog, **eval_kwargs) return fields, tup_isort
def _load_pts(lpti, qt, eval_fn, idx_tup, mask_kwargs, skydatas, dsns, **kwargs): lpts = [] sx = [] mask = np.all(idx_tup == qt, -1) for (lp, tidx), smx in zip(lpti, skydatas): if tidx >= 0: lpts.append(lp[tidx]) sx.append(smx[mask]) if mask_kwargs is not None: for k, v in mask_kwargs.items(): kwargs.update([(k, v[mask])]) return eval_fn(lpts, sx, dsns[mask], **kwargs)