classy-szfast 0.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. classy_szfast-0.0.0/.gitignore +6 -0
  2. classy_szfast-0.0.0/MANIFEST.in +1 -0
  3. classy_szfast-0.0.0/PKG-INFO +14 -0
  4. classy_szfast-0.0.0/README.md +4 -0
  5. classy_szfast-0.0.0/classy_szfast/__init__.py +14 -0
  6. classy_szfast-0.0.0/classy_szfast/classy_sz.py +547 -0
  7. classy_szfast-0.0.0/classy_szfast/classy_szfast.py +933 -0
  8. classy_szfast-0.0.0/classy_szfast/config.py +10 -0
  9. classy_szfast-0.0.0/classy_szfast/cosmopower.py +259 -0
  10. classy_szfast-0.0.0/classy_szfast/cosmosis_classy_szfast_interface.py +331 -0
  11. classy_szfast-0.0.0/classy_szfast/custom_bias/__init__.py +0 -0
  12. classy_szfast-0.0.0/classy_szfast/custom_bias/custom_bias.py +20 -0
  13. classy_szfast-0.0.0/classy_szfast/custom_profiles/__init__.py +0 -0
  14. classy_szfast-0.0.0/classy_szfast/custom_profiles/custom_profiles.py +45 -0
  15. classy_szfast-0.0.0/classy_szfast/pks_and_sigmas.py +150 -0
  16. classy_szfast-0.0.0/classy_szfast/restore_nn.py +395 -0
  17. classy_szfast-0.0.0/classy_szfast/suppress_warnings.py +10 -0
  18. classy_szfast-0.0.0/classy_szfast/utils.py +62 -0
  19. classy_szfast-0.0.0/classy_szfast.egg-info/PKG-INFO +14 -0
  20. classy_szfast-0.0.0/classy_szfast.egg-info/SOURCES.txt +27 -0
  21. classy_szfast-0.0.0/classy_szfast.egg-info/dependency_links.txt +1 -0
  22. classy_szfast-0.0.0/classy_szfast.egg-info/requires.txt +6 -0
  23. classy_szfast-0.0.0/classy_szfast.egg-info/top_level.txt +1 -0
  24. classy_szfast-0.0.0/pypi_upload.sh +44 -0
  25. classy_szfast-0.0.0/pyproject.toml +22 -0
  26. classy_szfast-0.0.0/requirements.txt +4 -0
  27. classy_szfast-0.0.0/setup.cfg +14 -0
  28. classy_szfast-0.0.0/setup.py +4 -0
@@ -0,0 +1,6 @@
1
+
2
+ .DS_Store
3
+ *egg-info
4
+ test_classy_szfast.py
5
+ test_classy_szfast-mnu3state.py
6
+ __pycache__
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,14 @@
1
+ Metadata-Version: 2.1
2
+ Name: classy_szfast
3
+ Version: 0.0.0
4
+ Summary: The accelerator of the class_sz code from https://github.com/CLASS-SZ
5
+ Maintainer-email: Boris Bolliet <bb667@cam.ac.uk>
6
+ Project-URL: Homepage, https://github.com/CLASS-SZ
7
+ Project-URL: GitHub, https://github.com/CLASS-SZ
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: numpy>=1.19.0
10
+ Requires-Dist: Cython>=0.29.21
11
+ Requires-Dist: tensorflow
12
+ Requires-Dist: cosmopower
13
+ Requires-Dist: mcfit
14
+ Requires-Dist: get_cosmopower_emus
@@ -0,0 +1,4 @@
1
+ Python module for the acceleration of class_sz and interface with likelihood codes.
2
+
3
+ see https://github.com/CLASS-SZ for details.
4
+
@@ -0,0 +1,14 @@
1
+ from classy_szfast.classy_szfast import Class_szfast
2
+ from .config import *
3
+ from .utils import *
4
+ from .cosmopower import *
5
+ from .pks_and_sigmas import *
6
+
7
+ from pathlib import Path
8
+ import sys
9
+
10
+
11
+ from .custom_profiles.custom_profiles import *
12
+
13
+
14
+ from .custom_bias.custom_bias import *
@@ -0,0 +1,547 @@
1
+ from cobaya.theories.classy import classy
2
+ from copy import deepcopy
3
+ from typing import NamedTuple, Sequence, Union, Optional
4
+ from cobaya.tools import load_module
5
+ import logging
6
+ import os
7
+ import numpy as np
8
+ import time
9
+
10
+ class classy_sz(classy):
11
+
12
+ use_class_sz_fast_mode = 1 # this is passed in the yaml file
13
+ use_class_sz_no_cosmo_mode = 0 # this is passed in the yaml file
14
+ lensing_lkl = 'ACT'
15
+
16
+ baos = None
17
+
18
+ def initialize(self):
19
+ """Importing CLASS from the correct path, if given, and if not, globally."""
20
+
21
+ self.classy_module = self.is_installed()
22
+ if not self.classy_module:
23
+ raise NotInstalledError(
24
+ self.log, "Could not find CLASS_SZ. Check error message above.")
25
+
26
+ from classy_sz import Class, CosmoSevereError, CosmoComputationError
27
+
28
+ global CosmoComputationError, CosmoSevereError
29
+
30
+ self.classy = Class()
31
+ super(classy,self).initialize()
32
+
33
+
34
+ # Add general CLASS stuff
35
+ self.extra_args["output"] = self.extra_args.get("output", "")
36
+
37
+ if "sBBN file" in self.extra_args:
38
+ self.extra_args["sBBN file"] = (
39
+ self.extra_args["sBBN file"].format(classy=self.path))
40
+
41
+ # Derived parameters that may not have been requested, but will be necessary later
42
+ self.derived_extra = []
43
+ self.log.info("Initialized!")
44
+
45
+
46
+ ## rename some parameters to avoid conflices
47
+ classy_sz_renames = {
48
+
49
+ 'omega_m':'Omega_m',
50
+ 'Omegam':'Omega_m',
51
+ 'Omega_m':'Omega_m'
52
+ }
53
+ self.renames.update(classy_sz_renames)
54
+
55
+
56
+ if self.use_class_sz_no_cosmo_mode == 1:
57
+
58
+ self.log.info("Initializing cosmology part!")
59
+
60
+ initial_parameters = self.extra_args.copy()
61
+
62
+
63
+ self.classy.set(initial_parameters)
64
+ self.classy.compute_class_szfast(likelihood_mode=True)
65
+ self.log.info("cosmology part initialized!")
66
+
67
+
68
+
69
+
70
+ def must_provide(self, **requirements):
71
+
72
+
73
+
74
+ if "Cl_sz" in requirements:
75
+ # make sure cobaya still runs as it does for standard classy
76
+ requirements.pop("Cl_sz")
77
+ # specify the method to collect the new observable
78
+ self.collectors["Cl_sz"] = Collector(
79
+ method="cl_sz", # name of the method in classy.pyx
80
+ args_names=[],
81
+ args=[])
82
+
83
+ if "Cl_yxg" in requirements:
84
+ # make sure cobaya still runs as it does for standard classy
85
+ requirements.pop("Cl_yxg")
86
+ # specify the method to collect the new observable
87
+ self.collectors["Cl_yxg"] = Collector(
88
+ method="cl_yg", # name of the method in classy.pyx
89
+ args_names=[],
90
+ args=[])
91
+
92
+ if "Cl_gxg" in requirements:
93
+ # make sure cobaya still runs as it does for standard classy
94
+ requirements.pop("Cl_gxg")
95
+ # specify the method to collect the new observable
96
+ self.collectors["Cl_gxg"] = Collector(
97
+ method="cl_gg", # name of the method in classy.pyx
98
+ args_names=[],
99
+ args=[])
100
+
101
+
102
+ if "Cl_gxmu" in requirements:
103
+ # make sure cobaya still runs as it does for standard classy
104
+ requirements.pop("Cl_gxmu")
105
+ # specify the method to collect the new observable
106
+ self.collectors["Cl_gxmu"] = Collector(
107
+ method="cl_gm", # name of the method in classy.pyx
108
+ args_names=[],
109
+ args=[])
110
+
111
+
112
+ if "Cl_muxmu" in requirements:
113
+ # make sure cobaya still runs as it does for standard classy
114
+ requirements.pop("Cl_muxmu")
115
+ # specify the method to collect the new observable
116
+ self.collectors["Cl_muxmu"] = Collector(
117
+ method="cl_mm", # name of the method in classy.pyx
118
+ args_names=[],
119
+ args=[])
120
+
121
+
122
+ if "Cl_kxg" in requirements:
123
+ # make sure cobaya still runs as it does for standard classy
124
+ requirements.pop("Cl_kxg")
125
+ # specify the method to collect the new observable
126
+ self.collectors["Cl_kxg"] = Collector(
127
+ method="cl_kg", # name of the method in classy.pyx
128
+ args_names=[],
129
+ args=[])
130
+
131
+ if "Cl_kxmu" in requirements:
132
+ # make sure cobaya still runs as it does for standard classy
133
+ requirements.pop("Cl_kxmu")
134
+ # specify the method to collect the new observable
135
+ self.collectors["Cl_kxmu"] = Collector(
136
+ method="cl_km", # name of the method in classy.pyx
137
+ args_names=[],
138
+ args=[])
139
+
140
+
141
+ if "Cl_yxmu" in requirements:
142
+ # make sure cobaya still runs as it does for standard classy
143
+ requirements.pop("Cl_yxmu")
144
+ # specify the method to collect the new observable
145
+ self.collectors["Cl_yxmu"] = Collector(
146
+ method="cl_ym", # name of the method in classy.pyx
147
+ args_names=[],
148
+ args=[])
149
+ if "Cl_kgxg" in requirements:
150
+ # make sure cobaya still runs as it does for standard classy
151
+ requirements.pop("Cl_kgxg")
152
+ # specify the method to collect the new observable
153
+ self.collectors["Cl_kgxg"] = Collector(
154
+ method="cl_ggamma", # name of the method in classy.pyx
155
+ args_names=[],
156
+ args=[])
157
+ if "Cl_kgxmu" in requirements:
158
+ # make sure cobaya still runs as it does for standard classy
159
+ requirements.pop("Cl_kgxmu")
160
+ # specify the method to collect the new observable
161
+ self.collectors["Cl_kgxmu"] = Collector(
162
+ method="cl_kg_m", # name of the method in classy.pyx
163
+ args_names=[],
164
+ args=[])
165
+ if "Cl_IAxg" in requirements:
166
+ # make sure cobaya still runs as it does for standard classy
167
+ requirements.pop("Cl_IAxg")
168
+ # specify the method to collect the new observable
169
+ self.collectors["Cl_IAxg"] = Collector(
170
+ method="cl_IA_g", # name of the method in classy.pyx
171
+ args_names=[],
172
+ args=[])
173
+
174
+ if "sz_binned_cluster_counts" in requirements:
175
+ # make sure cobaya still runs as it does for standard classy
176
+ requirements.pop("sz_binned_cluster_counts")
177
+ # specify the method to collect the new observable
178
+ self.collectors["sz_binned_cluster_counts"] = Collector(
179
+ method="dndzdy_theoretical", # name of the method in classy.pyx
180
+ args_names=[],
181
+ args=[])
182
+
183
+ if "sz_unbinned_cluster_counts" in requirements:
184
+ # make sure cobaya still runs as it does for standard classy
185
+ requirements.pop("sz_unbinned_cluster_counts")
186
+ # specify the method to collect the new observable
187
+ self.collectors["sz_unbinned_cluster_counts"] = Collector(
188
+ method="szcounts_ntot_rates_loglike", # name of the method in classy.pyx
189
+ args_names=[],
190
+ args=[])
191
+
192
+ if "cl_cib_kappa" in requirements:
193
+ # make sure cobaya still runs as it does for standard classy
194
+ requirements.pop("cl_cib_kappa")
195
+ # specify the method to collect the new observable
196
+ self.collectors["cl_cib_kappa"] = Collector(
197
+ method="cl_lens_cib", # name of the method in classy.pyx
198
+ args_names=[],
199
+ args=[])
200
+ if "cl_galn_galn" in requirements:
201
+ # make sure cobaya still runs as it does for standard classy
202
+ requirements.pop("cl_galn_galn")
203
+ # specify the method to collect the new observable
204
+ self.collectors["cl_galn_galn"] = Collector(
205
+ method="cl_galn_galn", # name of the method in classy.pyx
206
+ args_names=[],
207
+ args=[])
208
+ if "cl_galn_lens" in requirements:
209
+ # make sure cobaya still runs as it does for standard classy
210
+ requirements.pop("cl_galn_lens")
211
+ # specify the method to collect the new observable
212
+ self.collectors["cl_galn_lens"] = Collector(
213
+ method="cl_galn_lens", # name of the method in classy.pyx
214
+ args_names=[],
215
+ args=[])
216
+
217
+ if "Cl_galnxtsz" in requirements:
218
+ # make sure cobaya still runs as it does for standard classy
219
+ requirements.pop("Cl_galnxtsz")
220
+ # specify the method to collect the new observable
221
+ self.collectors["Cl_galnxtsz"] = Collector(
222
+ method="cl_galn_tsz", # name of the method in classy.pyx
223
+ args_names=[],
224
+ args=[])
225
+ if "Cl_galnxgallens" in requirements:
226
+ # make sure cobaya still runs as it does for standard classy
227
+ requirements.pop("Cl_galnxgallens")
228
+ # specify the method to collect the new observable
229
+ self.collectors["Cl_galnxgallens"] = Collector(
230
+ method="cl_galn_gallens", # name of the method in classy.pyx
231
+ args_names=[],
232
+ args=[])
233
+ if "Cl_lensmagnxtsz" in requirements:
234
+ # make sure cobaya still runs as it does for standard classy
235
+ requirements.pop("Cl_lensmagnxtsz")
236
+ # specify the method to collect the new observable
237
+ self.collectors["Cl_lensmagnxtsz"] = Collector(
238
+ method="cl_lensmagn_tsz", # name of the method in classy.pyx
239
+ args_names=[],
240
+ args=[])
241
+ if "Cl_lensmagnxgallens" in requirements:
242
+ # make sure cobaya still runs as it does for standard classy
243
+ requirements.pop("Cl_lensmagnxgallens")
244
+ # specify the method to collect the new observable
245
+ self.collectors["Cl_lensmagnxgallens"] = Collector(
246
+ method="cl_lensmagn_gallens", # name of the method in classy.pyx
247
+ args_names=[],
248
+ args=[])
249
+ if "Cl_galnxIA" in requirements:
250
+ # make sure cobaya still runs as it does for standard classy
251
+ requirements.pop("Cl_galnxIA")
252
+ # specify the method to collect the new observable
253
+ self.collectors["Cl_galnxIA"] = Collector(
254
+ method="cl_galn_IA", # name of the method in classy.pyx
255
+ args_names=[],
256
+ args=[])
257
+ super().must_provide(**requirements)
258
+
259
+
260
+ # get the required new observable
261
+ def get_Cl(self, ell_factor=False, units="FIRASmuK2"):
262
+
263
+ if self.use_class_sz_fast_mode:
264
+
265
+ return self.get_Clfast(ell_factor=ell_factor)
266
+
267
+ else:
268
+
269
+ return self._get_Cl(ell_factor=ell_factor, units=units, lensed=True)
270
+
271
+ def get_Clfast(self,ell_factor = False):
272
+
273
+ cls = {}
274
+ cls = deepcopy(self._current_state["Cl"])
275
+
276
+ lcp = np.asarray(cls['ell'])
277
+
278
+ if ell_factor==True:
279
+ cls['tt'] *= (2.7255e6)**2.*(lcp*(lcp+1.))/2./np.pi
280
+ cls['te'] *= (2.7255e6)**2.*(lcp*(lcp+1.))/2./np.pi
281
+ cls['ee'] *= (2.7255e6)**2.*(lcp*(lcp+1.))/2./np.pi
282
+
283
+ if self.lensing_lkl == "ACT" and ell_factor == False:
284
+ cls['tt'] *= (2.7255e6)**2.
285
+ cls['te'] *= (2.7255e6)**2.
286
+ cls['ee'] *= (2.7255e6)**2.
287
+
288
+ if self.lensing_lkl == "SOLikeT":
289
+ cls['pp'] *= (lcp*(lcp+1.))**2./4.
290
+
291
+ elif self.lensing_lkl == "ACT":
292
+ cls['pp'] *= 1.
293
+
294
+ else: # here for the planck lensing lkl, using lfactor option gives:
295
+ cls['pp'] *= (lcp*(lcp+1.))**2.*1./2./np.pi
296
+
297
+ return cls
298
+
299
+ # get the required new observable
300
+ def get_Cl_sz(self):
301
+ cls = {}
302
+ cls = deepcopy(self._current_state["Cl_sz"])
303
+ return cls
304
+
305
+ def get_Cl_yxg(self):
306
+ cls = {}
307
+ cls = deepcopy(self._current_state["Cl_yxg"])
308
+ return cls
309
+ def get_Cl_kxg(self):
310
+ cls = {}
311
+ cls = deepcopy(self._current_state["Cl_kxg"])
312
+ return cls
313
+ def get_Cl_gxg(self):
314
+ cls = {}
315
+ cls = deepcopy(self._current_state["Cl_gxg"])
316
+ return cls
317
+ def get_Cl_muxmu(self):
318
+ cls = {}
319
+ cls = deepcopy(self._current_state["Cl_muxmu"])
320
+ return cls
321
+ def get_Cl_gxmu(self):
322
+ cls = {}
323
+ cls = deepcopy(self._current_state["Cl_gxmu"])
324
+ return cls
325
+ def get_Cl_kxmu(self):
326
+ cls = {}
327
+ cls = deepcopy(self._current_state["Cl_kxmu"])
328
+ return cls
329
+ def get_Cl_yxmu(self):
330
+ cls = {}
331
+ cls = deepcopy(self._current_state["Cl_yxmu"])
332
+ return cls
333
+ def get_Cl_kgxg(self):
334
+ cls = {}
335
+ cls = deepcopy(self._current_state["Cl_kgxg"])
336
+ return cls
337
+ def get_Cl_kgxmu(self):
338
+ cls = {}
339
+ cls = deepcopy(self._current_state["Cl_kgxmu"])
340
+ return cls
341
+ def get_Cl_IAxg(self):
342
+ cls = {}
343
+ cls = deepcopy(self._current_state["Cl_IAxg"])
344
+ return cls
345
+ def get_cl_galn_lens(self):
346
+ cls = {}
347
+ cls = deepcopy(self._current_state["cl_galn_lens"])
348
+ return cls
349
+ def get_cl_galn_galn(self):
350
+ cls = {}
351
+ cls = deepcopy(self._current_state["cl_galn_galn"])
352
+ return cls
353
+ def get_Cl_galnxtsz(self):
354
+ cls = {}
355
+ cls = deepcopy(self._current_state["Cl_galnxtsz"])
356
+ return cls
357
+ def get_Cl_galnxgallens(self):
358
+ cls = {}
359
+ cls = deepcopy(self._current_state["Cl_galnxgallens"])
360
+ return cls
361
+ def get_Cl_lensmagnxtsz(self):
362
+ cls = {}
363
+ cls = deepcopy(self._current_state["Cl_lensmagnxtsz"])
364
+ return cls
365
+ def get_Cl_lensmagnxgallens(self):
366
+ cls = {}
367
+ cls = deepcopy(self._current_state["Cl_lensmagnxgallens"])
368
+ return cls
369
+ def get_Cl_galnxIA(self):
370
+ cls = {}
371
+ cls = deepcopy(self._current_state["Cl_galnxIA"])
372
+ return cls
373
+
374
+ # get the required new observable
375
+ def get_sz_unbinned_cluster_counts(self):
376
+
377
+ cls = deepcopy(self._current_state["sz_unbinned_cluster_counts"])
378
+
379
+ return cls['loglike'],cls['ntot'],cls['rates']
380
+
381
+
382
+ # get the required new observable
383
+ def get_sz_binned_cluster_counts(self):
384
+ cls = {}
385
+ cls = deepcopy(self._current_state["sz_binned_cluster_counts"])
386
+ return cls
387
+
388
+ def get_cl_cib_kappa(self):
389
+ cls = {}
390
+ cls = deepcopy(self._current_state["cl_cib_kappa"])
391
+ return cls
392
+
393
+ # IMPORTANT: this method is imported from cobaya and modified to accomodate the emulators
394
+ def calculate(self, state, want_derived=True, **params_values_dict):
395
+
396
+
397
+ params_values = params_values_dict.copy()
398
+ # if baos are requested we need to update the relevant flags
399
+ if self.baos:
400
+ params_values.update({'skip_chi':0,'skip_hubble':0})
401
+
402
+ if 'N_ncdm' in self.extra_args.keys():
403
+
404
+ if self.extra_args['N_ncdm'] == 3:
405
+
406
+ str_mncdm = str(params_values['m_ncdm'])
407
+ params_values['m_ncdm'] = str_mncdm+','+str_mncdm+','+str_mncdm
408
+
409
+ try:
410
+
411
+ params_values['ln10^{10}A_s'] = params_values.pop("logA")
412
+
413
+ self.set(params_values)
414
+
415
+ except KeyError:
416
+
417
+ self.set(params_values)
418
+
419
+ # Compute!
420
+ try:
421
+ if self.use_class_sz_fast_mode == 1:
422
+
423
+
424
+ if self.use_class_sz_no_cosmo_mode == 1:
425
+
426
+ start_time = time.time()
427
+ self.classy.compute_class_sz(params_values)
428
+ end_time = time.time()
429
+ # self.log.info("Execution time of class_sz: {:.5f} seconds".format(end_time - start_time))
430
+
431
+ else:
432
+
433
+ start_time = time.time()
434
+ self.classy.compute_class_szfast(likelihood_mode=True)
435
+ end_time = time.time()
436
+ # self.log.info("Execution time of class_szfast: {:.5f} seconds".format(end_time - start_time))
437
+ # print('pars in classy',self.classy.pars)
438
+ else:
439
+
440
+ self.classy.compute()
441
+
442
+ # "Valid" failure of CLASS: parameters too extreme -> log and report
443
+ except self.classy_module.CosmoComputationError as e:
444
+ if self.stop_at_error:
445
+ self.log.error(
446
+ "Computation error (see traceback below)! "
447
+ "Parameters sent to CLASS: %r and %r.\n"
448
+ "To ignore this kind of error, make 'stop_at_error: False'.",
449
+ state["params"], dict(self.extra_args))
450
+ raise
451
+ else:
452
+ self.log.debug("Computation of cosmological products failed. "
453
+ "Assigning 0 likelihood and going on. "
454
+ "The output of the CLASS error was %s" % e)
455
+ return False
456
+ # CLASS not correctly initialized, or input parameters not correct
457
+ except self.classy_module.CosmoSevereError:
458
+ self.log.error("Serious error setting parameters or computing results. "
459
+ "The parameters passed were %r and %r. To see the original "
460
+ "CLASS' error traceback, make 'debug: True'.",
461
+ state["params"], self.extra_args)
462
+ raise # No LoggedError, so that CLASS traceback gets printed
463
+ # Gather products
464
+ for product, collector in self.collectors.items():
465
+ # print(product,collector)
466
+ # Special case: sigma8 needs H0, which cannot be known beforehand:
467
+ if "sigma8" in self.collectors:
468
+ self.collectors["sigma8"].args[0] = 8 / self.classy.h()
469
+ method = getattr(self.classy, collector.method)
470
+ arg_array = self.collectors[product].arg_array
471
+ if isinstance(arg_array, int):
472
+ arg_array = np.atleast_1d(arg_array)
473
+ if arg_array is None:
474
+ state[product] = method(
475
+ *self.collectors[product].args, **self.collectors[product].kwargs)
476
+ elif isinstance(arg_array, Sequence) or isinstance(arg_array, np.ndarray):
477
+ arg_array = np.array(arg_array)
478
+ if len(arg_array.shape) == 1:
479
+ # if more than one vectorised arg, assume all vectorised in parallel
480
+ n_values = len(self.collectors[product].args[arg_array[0]])
481
+ state[product] = np.zeros(n_values)
482
+ args = deepcopy(list(self.collectors[product].args))
483
+ for i in range(n_values):
484
+ for arg_arr_index in arg_array:
485
+ args[arg_arr_index] = \
486
+ self.collectors[product].args[arg_arr_index][i]
487
+ state[product][i] = method(
488
+ *args, **self.collectors[product].kwargs)
489
+ elif len(arg_array.shape) == 2:
490
+ if len(arg_array) > 2:
491
+ raise NotImplementedError("Only 2 array expanded vars so far.")
492
+ # Create outer combinations
493
+ x_and_y = np.array(np.meshgrid(
494
+ self.collectors[product].args[arg_array[0, 0]],
495
+ self.collectors[product].args[arg_array[1, 0]])).T
496
+ args = deepcopy(list(self.collectors[product].args))
497
+ result = np.empty(shape=x_and_y.shape[:2])
498
+ for i, row in enumerate(x_and_y):
499
+ for j, column_element in enumerate(x_and_y[i]):
500
+ args[arg_array[0, 0]] = column_element[0]
501
+ args[arg_array[1, 0]] = column_element[1]
502
+ result[i, j] = method(
503
+ *args, **self.collectors[product].kwargs)
504
+ state[product] = (
505
+ self.collectors[product].args[arg_array[0, 0]],
506
+ self.collectors[product].args[arg_array[1, 0]], result)
507
+ else:
508
+ raise ValueError("arg_array not correctly formatted.")
509
+ elif arg_array in self.collectors[product].kwargs:
510
+ value = np.atleast_1d(self.collectors[product].kwargs[arg_array])
511
+ state[product] = np.zeros(value.shape)
512
+ for i, v in enumerate(value):
513
+ kwargs = deepcopy(self.collectors[product].kwargs)
514
+ kwargs[arg_array] = v
515
+ state[product][i] = method(
516
+ *self.collectors[product].args, **kwargs)
517
+ else:
518
+ raise LoggedError(self.log, "Variable over which to do an array call "
519
+ f"not known: arg_array={arg_array}")
520
+ if collector.post:
521
+ state[product] = collector.post(*state[product])
522
+ # Prepare derived parameters
523
+
524
+ d, d_extra = self._get_derived_all(derived_requested=want_derived)
525
+
526
+ if want_derived:
527
+
528
+ state["derived"] = {p: d.get(p) for p in self.output_params}
529
+
530
+
531
+ state["derived_extra"] = deepcopy(d_extra)
532
+
533
+
534
+
535
+ @classmethod
536
+ def is_installed(cls, **kwargs):
537
+ return load_module('classy_sz')
538
+
539
+
540
+ # this just need to be there as it's used to fill-in self.collectors in must_provide:
541
+ class Collector(NamedTuple):
542
+ method: str
543
+ args: Sequence = []
544
+ args_names: Sequence = []
545
+ kwargs: dict = {}
546
+ arg_array: Union[int, Sequence] = None
547
+ post: Optional[callable] = None