diff --git a/python/snewpy/models/ccsn.py b/python/snewpy/models/ccsn.py index ee95e249..65178b52 100644 --- a/python/snewpy/models/ccsn.py +++ b/python/snewpy/models/ccsn.py @@ -3,9 +3,7 @@ The submodule ``snewpy.models.ccsn`` contains models of core-collapse supernovae derived from the :class:`SupernovaModel` base class. -Since SNEWPY v1.3, the prefered method is to initialise a model based on its physics parameters. -Initialisation from a file name is deprecated. - +Models are initialised based on their physics parameters. Use the ``param`` class property to view all physics parameters and their possible values: >>> from snewpy.models.ccsn import Nakazato_2013 @@ -40,11 +38,9 @@ class method to get a list of all valid combinations and filter it: from .base import PinchedModel from snewpy.models.registry_model import RegistryModel, Parameter -from snewpy.models.registry_model import deprecated, legacy_filename_initialization from snewpy.models.registry_model import all_models from textwrap import dedent -@legacy_filename_initialization @RegistryModel( progenitor_mass = [13, 20, 30, 50] * u.Msun, revival_time = [0, 100, 200, 300] * u.ms, @@ -61,23 +57,6 @@ class Nakazato_2013(loaders.Nakazato_2013): (2013), ApJ 804:75 (2015), PASJ 73:639 (2021). See also http://asphwww.ph.noda.tus.ac.jp/snn/. """ - def _metadata_from_filename(self, filename:str)->dict: - metadata = {'Progenitor mass': float(filename.split('-')[-1].strip('s%.fits')) * u.Msun} - if 't_rev' in filename: - metadata.update({ - 'EOS': filename.split('-')[-4].upper(), - 'Metallicity': float(filename.split('-')[-3].strip('z%')), - 'Revival time': float(filename.split('-')[-2].strip('t_rev%ms')) * u.ms - }) - # No revival time because the explosion "failed" (BH formation). - else: - metadata.update({ - 'EOS': filename.split('-')[-4].upper(), - 'Metallicity': float(filename.split('-')[-2].strip('z%')), - 'Revival time': 0 * u.ms - }) - return metadata - def __init__(self, progenitor_mass:u.Quantity, revival_time:u.Quantity, metallicity:float, eos:str): # Strip units for filename construction progenitor_mass = progenitor_mass.to(u.Msun).value @@ -92,7 +71,6 @@ def __init__(self, progenitor_mass:u.Quantity, revival_time:u.Quantity, metallic return super().__init__(filename, self.metadata) -@legacy_filename_initialization @RegistryModel( progenitor_mass = [27., 9.6] * u.Msun, eos = ['LS220', 'SFHo'] @@ -100,13 +78,6 @@ def __init__(self, progenitor_mass:u.Quantity, revival_time:u.Quantity, metallic class Sukhbold_2015(loaders.Sukhbold_2015): """Model based on simulations from Sukhbold et al., ApJ 821:38,2016. Models were shared privately by email. """ - def _metadata_from_filename(self, filename:str): - metadata = { - 'Progenitor mass': float(filename.split('-')[-1].strip('z%.fits')) * u.Msun, - 'EOS': filename.split('-')[-2] - } - return metadata - def __init__(self, progenitor_mass:u.Quantity, eos:str): if progenitor_mass.value == 9.6: filename = f'sukhbold-{eos}-z{progenitor_mass.value:3.1f}.fits' @@ -115,7 +86,6 @@ def __init__(self, progenitor_mass:u.Quantity, eos:str): return super().__init__(filename, self.metadata) -@legacy_filename_initialization @RegistryModel( progenitor_mass = [11.2, 20., 27.] * u.Msun, direction = [1,2,3], @@ -133,7 +103,6 @@ def __init__(self, *, progenitor_mass:u.Quantity, direction:int): # Metadata is handled by __init__ in _GarchingArchiveModel return super().__init__(filename=filename, metadata=self.metadata) -@legacy_filename_initialization @RegistryModel( progenitor_mass= [11.2, 27.] * u.Msun, eos = ['LS220'] @@ -146,7 +115,6 @@ def __init__(self, progenitor_mass:u.Quantity, eos:str='LS220'): filename = f's{progenitor_mass.value:3.1f}c' return super().__init__(filename=filename, metadata=self.metadata) -@legacy_filename_initialization @RegistryModel( progenitor_mass = [15.] * u.Msun, rotation = ['fast','slow','non'], @@ -164,7 +132,6 @@ def __init__(self, *, progenitor_mass:u.Quantity, rotation:str, direction:int): return super().__init__(filename=filename, metadata=self.metadata) -@legacy_filename_initialization @RegistryModel( progenitor_mass = [40., 75.] * u.Msun, direction = [1,2,3], @@ -190,7 +157,7 @@ def __init__(self, * ,progenitor_mass:u.Quantity, direction:int): ), eos = ['HShen', 'LS220'] ) -class _OConnor_2013_new(loaders.OConnor_2013): +class OConnor_2013(loaders.OConnor_2013): """Model based on the black hole formation simulation in `O'Connor & Ott (2013) `_. """ def __init__(self, eos:str, progenitor_mass:u.Quantity): @@ -198,69 +165,31 @@ def __init__(self, eos:str, progenitor_mass:u.Quantity): filename = f'{eos}_timeseries.tar.gz' return super().__init__(filename=filename, metadata=self.metadata) -class OConnor_2013(legacy_filename_initialization(_OConnor_2013_new)): - _config_path='ccsn.OConnor_2013' - - @deprecated('base', 'mass') - def __init__(self, base=None, mass=None, eos='LS220', *, progenitor_mass=None): - """ - Parameters - ---------- - base : str - Absolute or relative path folder with model data. This argument is deprecated. - mass: int - Mass of model progenitor in units Msun. This argument is deprecated. - """ - if mass: - progenitor_mass = mass*u.Msun - if base: - self.metadata = {'Progenitor mass': progenitor_mass, - 'EOS': eos} - filename = f'{base}/{eos}_timeseries.tar.gz' - return super().__init__(filename=filename, eos=eos, progenitor_mass=progenitor_mass) - else: - return super().__init__(eos=eos, progenitor_mass=progenitor_mass) - -OConnor_2013.__init__.__doc__=dedent(OConnor_2013.__init__.__doc__)+_OConnor_2013_new.__init__.__doc__ -#make sure that only the latest class is present in the models table -all_models.remove(OConnor_2013.__mro__[1]) -all_models.add(OConnor_2013) - -@deprecated('eos') -@legacy_filename_initialization @RegistryModel( progenitor_mass = [40 * u.Msun], - eos=['LS220'] ) class OConnor_2015(loaders.OConnor_2015): """Model based on the black hole formation simulation in `O'Connor (2015) `_. """ def __init__(self, progenitor_mass:u.Quantity): + self.metadata["EOS"] = "LS220" # Filename is currently the same regardless of parameters filename = 'M1_neutrinos.dat' return super().__init__(filename, self.metadata) -@deprecated('eos') -@legacy_filename_initialization @RegistryModel( progenitor_mass = Parameter(values=(list(range(16, 27)) + [19.89, 22.39, 30, 33]) * u.Msun, desc_values = '[16..26, 19.89, 22.39, 30, 33] solMass' ), - eos = ['STOS_B145'] ) class Zha_2021(loaders.Zha_2021): """Model based on the hadron-quark phse transition models from `Zha et al. 2021 `_. """ - def _metadata_from_filename(self, filename:str)->dict: - metadata = {'Progenitor mass': float(os.path.splitext(os.path.basename(filename))[0][1:]) * u.Msun} - return metadata - def __init__(self, *, progenitor_mass:u.Quantity): + self.metadata["EOS"] = "STOS_B145" filename = f's{progenitor_mass.value:g}.dat' return super().__init__(filename, self.metadata) -@deprecated('eos') -@legacy_filename_initialization @RegistryModel( progenitor_mass=Parameter(np.concatenate((np.linspace(9.0, 12.75, 16), np.linspace(13, 30., 171), @@ -274,21 +203,14 @@ def __init__(self, *, progenitor_mass:u.Quantity): label='Turb. mixing param.', description='Turbulent mixing parameter alpha_lambda', ), - eos=['SFHo'] ) class Warren_2020(loaders.Warren_2020): """Model based on simulations from Warren et al., ApJ 898:139, 2020. Neutrino fluxes available at https://doi.org/10.5281/zenodo.3667908.""" # np.arange with decimal increments can produce floating point errors # Though it may be more intuitive to use np.arange, these fp-errors quickly become troublesome - def _metadata_from_filename(self, filename:str)->dict: - _, _, turbmixing_param, progenitor_mass = os.path.splitext(os.path.basename(filename))[0].split('_') - metadata = {'Progenitor mass': float(progenitor_mass[1:]) * u.Msun, - 'Turb. mixing param.': float(turbmixing_param[1:]), - 'EOS': 'SFHo'} - return metadata - def __init__(self, *, progenitor_mass, turbmixing_param): + self.metadata["EOS"] = "SFHo" if progenitor_mass.value.is_integer() and progenitor_mass.value <= 30.: fname = os.path.join(f'stir_a{turbmixing_param:3.2f}', f'stir_multimessenger_a{turbmixing_param:3.2f}_m{progenitor_mass.value:.1f}.h5') @@ -297,36 +219,22 @@ def __init__(self, *, progenitor_mass, turbmixing_param): f'stir_multimessenger_a{turbmixing_param:3.2f}_m{progenitor_mass.value:g}.h5') return super().__init__(fname, self.metadata) -@deprecated('eos','mass') -@legacy_filename_initialization @RegistryModel( rotational_velocity= [0, 1] * u.rad / u.s, magnetic_field_exponent= Parameter([0, 12, 13], label='B_0 Exponent', description='Exponent of magnetic field (See Eq. 46)'), - eos=['LS220'], progenitor_mass=[20*u.Msun], _param_validator = lambda p: (p['rotational_velocity'].value == 1 and p['magnetic_field_exponent'] in (12, 13)) or \ (p['rotational_velocity'].value == 0 and p['magnetic_field_exponent'] == 0) ) class Kuroda_2020(loaders.Kuroda_2020): """Model based on simulations from `Kuroda et al. (2020) `_.""" - - def _metadata_from_filename(self, filename:str)->dict: - _, rotational_velocity, magnetic_field_exponent = re.split('R|B', os.path.splitext(os.path.basename(filename))[0]) - metadata = { - 'Progenitor mass': 20 * u.Msun, - 'Rotational Velocity': int(rotational_velocity[0]), - 'B_0 Exponent': int(magnetic_field_exponent), - 'EOS': 'LS220' - } - return metadata - - def __init__(self, mass=None, *, rotational_velocity, magnetic_field_exponent): + def __init__(self, *, rotational_velocity, magnetic_field_exponent): + self.metadata["EOS"] = "LS220" filename = f'LnuR{int(rotational_velocity.value):1d}0B{int(magnetic_field_exponent):02d}.dat' return super().__init__(filename, self.metadata) -@legacy_filename_initialization @RegistryModel( progenitor_mass=[9, 10, 12, 13, 14, 15, 16, 19, 25, 60] * u.Msun, ) @@ -334,11 +242,6 @@ class Fornax_2019(loaders.Fornax_2019): """Model based on 3D simulations from D. Vartanyan, A. Burrows, D. Radice, M. A. Skinner and J. Dolence, MNRAS 482(1):351, 2019. Data available at https://www.astro.princeton.edu/~burrows/nu-emissions.3d/ """ - def _metadata_from_filename(self, filename:str)->dict: - progenitor_mass = os.path.splitext(os.path.basename(filename))[0].split('_')[2] - metadata = {'Progenitor mass': int(progenitor_mass[:-1]) * u.Msun} - return metadata - def __init__(self, cache_flux=False, *, progenitor_mass): """ Parameters @@ -352,7 +255,6 @@ def __init__(self, cache_flux=False, *, progenitor_mass): filename = f'lum_spec_{int(progenitor_mass.value):d}M.h5' return super().__init__(filename, self.metadata, cache_flux=cache_flux) -@legacy_filename_initialization @RegistryModel( progenitor_mass = Parameter((list(range(12, 24)) + [25, 26, 26.99]) * u.Msun, desc_values='[12..23, 25, 26, 26.99] solMass') @@ -361,11 +263,6 @@ class Fornax_2021(loaders.Fornax_2021): """Model based on 3D simulations from D. Vartanyan, A. Burrows, D. Radice, M. A. Skinner and J. Dolence, MNRAS 482(1):351, 2019. Data available at https://www.astro.princeton.edu/~burrows/nu-emissions.3d/ """ - def _metadata_from_filename(self, filename:str)->dict: - progenitor_mass = os.path.splitext(os.path.basename(filename))[0].split('_')[2] - metadata = {'Progenitor mass': float(progenitor_mass[:-1]) * u.Msun} - return metadata - def __init__(self, progenitor_mass:u.Quantity): # Load from Parameters if progenitor_mass.value.is_integer(): diff --git a/python/snewpy/models/registry_model.py b/python/snewpy/models/registry_model.py index 5e578acf..8e49b5b6 100644 --- a/python/snewpy/models/registry_model.py +++ b/python/snewpy/models/registry_model.py @@ -12,7 +12,6 @@ from functools import wraps import itertools as it import inspect -import os from textwrap import dedent from warnings import warn import numpy as np @@ -65,41 +64,6 @@ def _wrapper(obj): else: return func_decorator(obj) return _wrapper - -def deprecated(*names, message='Argument `{name}` is deprecated.'): - """A function decorator to issue a deprecation warning if a given argument is provided in the wrapped function call. - - Parameters - ---------- - - names: list of str - argument names which are deprecated - message: str - a template with {name} parameter, to make the message for each argument. - - .. Example:: - - @deprecated('foo','bar', message='Argument `{name}` is deprecated and will be removed in SNEWPYv2.0!') - def test_function(*, foo=1, bar=2, baz=3): - pass - - #calling test_function(foo=1, baz=3) will issue a deprecation warning: - # DeprecationWarning: Argument `foo` is deprecated and will be removed in SNEWPYv2.0! - """ - @_can_decorate_class_or_func - def _wrapper(func): - #get function signature - S = inspect.signature(func) - @wraps(func) - def _f(*args, **kwargs): - #bind signature to find all the parameters - params = S.bind(*args,**kwargs) - for name in names: - if name in params.arguments: - warn(message.format(name=name), category=UserWarning, stacklevel=2) - return func(*args,**kwargs) - return _f - return _wrapper def map_arguments(**names_dict): """map function arguments to create a function with new signature, @@ -378,7 +342,6 @@ def RegistryModel(_param_validator=None, **params): * Validation of the input user parameters (see :meth:`RegistryModel.validate`) * Generated constructor docstring based on allowed parameters * Populates the `self.metadata` from the given user parameters - * Optional (deprecated) "filename" argument, and calls initialization from the filename The decorated class: * *must* inherit from the loader class @@ -387,10 +350,8 @@ def RegistryModel(_param_validator=None, **params): 1. defines the filename from the given user parameters 2. optionally modify the `self.metadata` dictionary, and 3. calls the corresponding loader class constructor - * *can* define the ``_metadata_from_filename (self, filename:str)->dict``, to help populate the metadata when a `filename` argument is passed If a parameter has a single allowed value (i.e. fixed), this value will be default for this parameter in constructor. - """ pset:ParameterSet = ParameterSet(param_validator=_param_validator, **params) def _wrap(base_class): @@ -460,48 +421,3 @@ def get_param_combinations(cls)->tuple: all_models.add(c) return c return _wrap - -def legacy_filename_initialization(c): - """Wrap the model class, adding a filename argument in the init""" - - @deprecated('filename') - class c1(c): - _loader_class = c.__mro__[2] #store the loader class for later use - - def __init__(self, filename:str=None, *args, **kwargs): - if filename is not None: - if not hasattr(self,'metadata'): - self.metadata = {} - if hasattr(self,'_metadata_from_filename'): - self.metadata.update(self._metadata_from_filename(filename)) - self._loader_class.__init__(self, filename=os.path.abspath(filename), metadata=self.metadata) - else: - super().__init__(*args, **kwargs) - - #generate the docstring - c1.__doc__ = c.__doc__ - c1._doc_params_ = {'Parameters': - """filename: str\n Absolute or relative path to the file with model data. This argument is deprecated.""", - **c._doc_params_} - c1.__init__.__doc__ = c1._generate_docstring() - #update the call signature - S = inspect.signature(c) - S1 = inspect.signature(c1.__init__) - #set default values to None if they are not set - other_params = [] - for p in S.parameters.values(): - if p.default==p.empty: - p = p.replace(default=None) - other_params+=[p] - params = [S1.parameters['self'],S1.parameters['filename'],*other_params] - #fill the constructor signature - c1.__init__.__signature__ = S.replace(parameters=params) - #fill the class and module name to be the same as in class - c1.__qualname__ = c.__qualname__ - c1.__name__ = c.__name__ - c1.__module__ = c.__module__ - #register the model in the list - global all_models - all_models.remove(c) - all_models.add(c1) - return c1 diff --git a/python/snewpy/snowglobes.py b/python/snewpy/snowglobes.py index 134813dc..7b2b4216 100644 --- a/python/snewpy/snowglobes.py +++ b/python/snewpy/snowglobes.py @@ -129,7 +129,7 @@ def generate_fluence(model_path, model_type, transformation_type, d, output_file str Path of NumPy archive file with neutrino fluence data. """ - model_class = getattr(snewpy.models.ccsn, model_type) + model_class = getattr(snewpy.models.ccsn_loaders, model_type) # Choose flavor transformation. Use dict to associate the transformation name with its class. flavor_transformation_dict = {'NoTransformation': NoTransformation(), 'AdiabaticMSW_NMO': AdiabaticMSW(mh=MassHierarchy.NORMAL), 'AdiabaticMSW_IMO': AdiabaticMSW(mh=MassHierarchy.INVERTED), 'NonAdiabaticMSWH_NMO': NonAdiabaticMSWH(mh=MassHierarchy.NORMAL), 'NonAdiabaticMSWH_IMO': NonAdiabaticMSWH(mh=MassHierarchy.INVERTED), 'TwoFlavorDecoherence': TwoFlavorDecoherence(), 'ThreeFlavorDecoherence': ThreeFlavorDecoherence(), 'NeutrinoDecay_NMO': NeutrinoDecay(mh=MassHierarchy.NORMAL), 'NeutrinoDecay_IMO': NeutrinoDecay(mh=MassHierarchy.INVERTED), 'QuantumDecoherence_NMO': QuantumDecoherence(mh=MassHierarchy.NORMAL), 'QuantumDecoherence_IMO': QuantumDecoherence(mh=MassHierarchy.INVERTED)} diff --git a/python/snewpy/test/test_01_registry.py b/python/snewpy/test/test_01_registry.py index 4bbaf7fe..547dd41d 100644 --- a/python/snewpy/test/test_01_registry.py +++ b/python/snewpy/test/test_01_registry.py @@ -117,7 +117,7 @@ def test_OConnor_2015(self): """ Instantiate a set of "O'Connor 2015" models """ - model = OConnor_2015(progenitor_mass=40*u.Msun, eos='LS220') + model = OConnor_2015(progenitor_mass=40*u.Msun) self.assertEqual(model.metadata['EOS'], 'LS220') self.assertEqual(model.metadata['Progenitor mass'], 40*u.Msun) @@ -274,7 +274,7 @@ def test_Kuroda_2020(self): (1, [12, 13])]: for exponent in exponents: model = Kuroda_2020(rotational_velocity=rot_vel * u.rad / u.s, - magnetic_field_exponent=exponent, eos='LS220') + magnetic_field_exponent=exponent) self.assertEqual(model.metadata['EOS'], 'LS220') self.assertEqual(model.metadata['Progenitor mass'], 20*u.Msun) @@ -314,7 +314,7 @@ def test_Zha_2021(self): """ for mass in list(range(16, 27)) + [19.89, 22.39, 30, 33]: - model = Zha_2021(progenitor_mass=mass * u.Msun, eos='STOS_B145') + model = Zha_2021(progenitor_mass=mass * u.Msun) self.assertEqual(model.metadata['Progenitor mass'], float(mass)*u.Msun) self.assertEqual(model.metadata['EOS'], 'STOS_B145')