From a3ea33b8005eca7ad5b02db8effc2412883f44f9 Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Wed, 1 Oct 2025 09:21:35 +0100 Subject: [PATCH 01/23] Lower required precision between cumsum and sum quantities Running on L200m6 crashed for half light radii (difference was 0.13% instead of less than 0.1%) --- SOAP/property_calculation/half_mass_radius.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SOAP/property_calculation/half_mass_radius.py b/SOAP/property_calculation/half_mass_radius.py index 1daecf1d..13896b16 100644 --- a/SOAP/property_calculation/half_mass_radius.py +++ b/SOAP/property_calculation/half_mass_radius.py @@ -52,7 +52,7 @@ def get_half_weight_radius( # Consistency check. # np.sum() and np.cumsum() use different orders, so we have to allow for # some small difference. - if cumulative_weights[-1] < 0.999 * total_weight: + if cumulative_weights[-1] < 0.998 * total_weight: raise RuntimeError( "Weights sum up to less than the given total weight value:" f" cumulative_weights[-1] = {cumulative_weights[-1]}," From a8a53c0e5a64e507b7edaf92b069cc3ff93bead2 Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Wed, 1 Oct 2025 09:46:43 +0100 Subject: [PATCH 02/23] Implement helper function for mass-weighted rotational velocity --- SOAP/particle_selection/subhalo_properties.py | 5 ++- .../kinematic_properties.py | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/SOAP/particle_selection/subhalo_properties.py b/SOAP/particle_selection/subhalo_properties.py index a11231f5..b4336554 100644 --- a/SOAP/particle_selection/subhalo_properties.py +++ b/SOAP/particle_selection/subhalo_properties.py @@ -33,6 +33,7 @@ get_angular_momentum_and_kappa_corot_luminosity_weighted, get_vmax, get_velocity_dispersion_matrix, + get_rotation_velocity_mass_weighted, ) from SOAP.property_calculation.inertia_tensors import ( get_inertia_tensor_mass_weighted, @@ -1422,9 +1423,7 @@ def star_cylindrical_velocities(self) -> unyt.unyt_array: def StellarRotationalVelocity(self) -> unyt.unyt_array: if (self.Nstar < 2) or (np.sum(self.Lstar) == 0): return None - v_cylindrical = self.star_cylindrical_velocities - v_phi = v_cylindrical[:, 1] - return (self.star_mass_fraction * v_phi).sum() + return get_rotation_velocity_mass_weighted(self.mass_star, self.star_cylindrical_velocities[:,1]) @lazy_property def stellar_cylindrical_squared_velocity_dispersion_vector(self) -> unyt.unyt_array: diff --git a/SOAP/property_calculation/kinematic_properties.py b/SOAP/property_calculation/kinematic_properties.py index 14aeab68..cd561c9b 100644 --- a/SOAP/property_calculation/kinematic_properties.py +++ b/SOAP/property_calculation/kinematic_properties.py @@ -14,6 +14,38 @@ import unyt +def get_weighted_rotation_velocity(particle_weights: unyt.unyt_array, + particle_azimuthal_velocities: unyt.unyt_array) -> unyt.unyt_quantity: + """ + Get the weighted average azimuthal velocity of a particle distribution. + + Parameters: + - particle_weights: unyt.unyt_array + Weight assigned to each particle. + - particle_azimuthal_velocities: unyt.unyt_array + Azimuthal velocity of each particle. + + Returns: + - Weighted average of the azimuthal velocity of particles. + """ + return (particle_weights * particle_azimuthal_velocities).sum() + +def get_rotation_velocity_mass_weighted(particle_masses, particle_azimuthal_velocities) -> unyt.unyt_quantity: + """ + Return the mass-weighted average azimuthal velocity of a particle distribution. + + Parameters: + - particle_masses: unyt.unyt_array + Mass of particle. + - particle_azimuthal_velocities: unyt.unyt_array + Azimuthal velocity of each particle. + + Returns: + - Mass-weighted average of the azimuthal velocity of particles. + """ + mass_weights = particle_masses / particle_masses.sum() + return get_weighted_rotation_velocity(mass_weights, particle_azimuthal_velocities) + def get_velocity_dispersion_matrix( mass_fraction: unyt.unyt_array, velocity: unyt.unyt_array, From 46c77a0b2a49a3e3a23a08d7e3e4e40db81aeab0 Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Wed, 1 Oct 2025 09:53:42 +0100 Subject: [PATCH 03/23] Define azimuthal velocity for subhalo apertures. --- .../particle_selection/aperture_properties.py | 46 +++++++++++++++++++ parameter_files/COLIBRE_HYBRID.yml | 1 + parameter_files/COLIBRE_THERMAL.yml | 1 + 3 files changed, 48 insertions(+) diff --git a/SOAP/particle_selection/aperture_properties.py b/SOAP/particle_selection/aperture_properties.py index 6a07f6cb..04f9c082 100644 --- a/SOAP/particle_selection/aperture_properties.py +++ b/SOAP/particle_selection/aperture_properties.py @@ -147,6 +147,7 @@ def MetalFracStar(self): ) from SOAP.property_calculation.kinematic_properties import ( get_velocity_dispersion_matrix, + get_rotation_velocity_mass_weighted, get_angular_momentum, get_angular_momentum_and_kappa_corot_mass_weighted, get_angular_momentum_and_kappa_corot_luminosity_weighted, @@ -156,6 +157,9 @@ def MetalFracStar(self): get_inertia_tensor_mass_weighted, get_inertia_tensor_luminosity_weighted, ) +from SOAP.property_calculation.cylindrical_coordinates import ( + calculate_cylindrical_velocities, +) from SOAP.core.swift_cells import SWIFTCellGrid from SOAP.property_calculation.stellar_age_calculator import StellarAgeCalculator from SOAP.particle_filter.cold_dense_gas_filter import ColdDenseGasFilter @@ -1448,6 +1452,47 @@ def veldisp_matrix_star(self) -> unyt.unyt_array: self.star_mass_fraction, self.vel_star, self.vcom_star ) + @lazy_property + def star_cylindrical_velocities(self) -> unyt.unyt_array: + """ + Calculate the velocities of the star particles in cyclindrical + coordinates, where the axes are centred on the stellar CoM, + and the z axis is aligned with the stellar angular momentum. + """ + + # We need at least 2 particles to have an angular momentum vector + if self.Nstar < 2: + return None + + # This can happen if we have particles on top of each other + # or with the same velocity + if np.sum(self.Lstar) == 0: + return None + + # Calculate the position of the stars relative to their CoM + pos = self.pos_star - (self.star_mass_fraction[:, None] * self.pos_star).sum( + axis=0 + ) + + # Calculate relative velocity of stars + vrel = self.vel_star - self.vcom[None, :] + + # Get velocities in cylindrical coordinates + return calculate_cylindrical_velocities( + pos, + vrel, + self.Lstar, + ) + + @lazy_property + def StellarRotationalVelocity(self) -> unyt.unyt_array: + """ + Mass-weighted average azimuthal velocity of stars. + """ + if (self.Nstar < 2) or (np.sum(self.Lstar) == 0): + return None + return get_rotation_velocity_mass_weighted(self.mass_star, self.star_cylindrical_velocities[:,1]) + @lazy_property def KineticEnergyStars(self) -> unyt.unyt_quantity: """ @@ -3583,6 +3628,7 @@ class ApertureProperties(HaloProperty): "veldisp_matrix_gas": False, "veldisp_matrix_dm": False, "veldisp_matrix_star": False, + "StellarRotationalVelocity": False, "KineticEnergyGas": False, "KineticEnergyStars": False, "Mgas_SF": False, diff --git a/parameter_files/COLIBRE_HYBRID.yml b/parameter_files/COLIBRE_HYBRID.yml index a8de3712..0662c16d 100644 --- a/parameter_files/COLIBRE_HYBRID.yml +++ b/parameter_files/COLIBRE_HYBRID.yml @@ -236,6 +236,7 @@ ApertureProperties: StellarMassFractionInMetals: true StellarMassFractionInOxygen: true StellarVelocityDispersionMatrix: general + StellarRotationalVelocity: false TotalMass: true TotalSNIaRate: true GasMassInColdDenseDiffuseMetals: diff --git a/parameter_files/COLIBRE_THERMAL.yml b/parameter_files/COLIBRE_THERMAL.yml index 8b2ddb4e..4bba494a 100644 --- a/parameter_files/COLIBRE_THERMAL.yml +++ b/parameter_files/COLIBRE_THERMAL.yml @@ -236,6 +236,7 @@ ApertureProperties: StellarMassFractionInMetals: true StellarMassFractionInOxygen: true StellarVelocityDispersionMatrix: general + StellarRotationalVelocity: false TotalMass: true TotalSNIaRate: true GasMassInColdDenseDiffuseMetals: From 77e862e992d60fb749a46295d2eb827c3dd2f0be Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Wed, 1 Oct 2025 10:30:08 +0100 Subject: [PATCH 04/23] Add helper function for cylindrical velocity dispersion vector --- SOAP/particle_selection/subhalo_properties.py | 25 ++++------- .../kinematic_properties.py | 43 +++++++++++++++++++ 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/SOAP/particle_selection/subhalo_properties.py b/SOAP/particle_selection/subhalo_properties.py index b4336554..d6ee10cb 100644 --- a/SOAP/particle_selection/subhalo_properties.py +++ b/SOAP/particle_selection/subhalo_properties.py @@ -33,6 +33,7 @@ get_angular_momentum_and_kappa_corot_luminosity_weighted, get_vmax, get_velocity_dispersion_matrix, + get_cylindrical_velocity_dispersion_vector_mass_weighted, get_rotation_velocity_mass_weighted, ) from SOAP.property_calculation.inertia_tensors import ( @@ -1426,39 +1427,31 @@ def StellarRotationalVelocity(self) -> unyt.unyt_array: return get_rotation_velocity_mass_weighted(self.mass_star, self.star_cylindrical_velocities[:,1]) @lazy_property - def stellar_cylindrical_squared_velocity_dispersion_vector(self) -> unyt.unyt_array: + def StellarCylindricalVelocityDispersionVector(self) -> unyt.unyt_array: if (self.Nstar < 2) or (np.sum(self.Lstar) == 0): return None - v_cylindrical = self.star_cylindrical_velocities - - # This implementation of standard deviation is more numerically stable than using - ^2 - mean_velocity = (self.star_mass_fraction[:, None] * v_cylindrical).sum(axis=0) - squared_velocity_dispersion = ( - self.star_mass_fraction[:, None] * (v_cylindrical - mean_velocity) ** 2 - ).sum(axis=0) - - return squared_velocity_dispersion + return get_cylindrical_velocity_dispersion_vector_mass_weighted(self.mass_star, self.star_cylindrical_velocities) @lazy_property def StellarCylindricalVelocityDispersion(self) -> unyt.unyt_array: - if self.stellar_cylindrical_squared_velocity_dispersion_vector is None: + if self.StellarCylindricalVelocityDispersionVector is None: return None return np.sqrt( - self.stellar_cylindrical_squared_velocity_dispersion_vector.sum() / 3 + (self.StellarCylindricalVelocityDispersionVector**2).sum() / 3 ) @lazy_property def StellarCylindricalVelocityDispersionVertical(self) -> unyt.unyt_array: - if self.stellar_cylindrical_squared_velocity_dispersion_vector is None: + if self.StellarCylindricalVelocityDispersionVector is None: return None - return np.sqrt(self.stellar_cylindrical_squared_velocity_dispersion_vector[2]) + return self.StellarCylindricalVelocityDispersionVector[2] @lazy_property def StellarCylindricalVelocityDispersionDiscPlane(self) -> unyt.unyt_array: - if self.stellar_cylindrical_squared_velocity_dispersion_vector is None: + if self.StellarCylindricalVelocityDispersionVector is None: return None return np.sqrt( - self.stellar_cylindrical_squared_velocity_dispersion_vector[:2].sum() + (self.StellarCylindricalVelocityDispersionVector[:2]**2).sum() ) @lazy_property diff --git a/SOAP/property_calculation/kinematic_properties.py b/SOAP/property_calculation/kinematic_properties.py index cd561c9b..da11b3de 100644 --- a/SOAP/property_calculation/kinematic_properties.py +++ b/SOAP/property_calculation/kinematic_properties.py @@ -84,6 +84,49 @@ def get_velocity_dispersion_matrix( return result +def get_weighted_cylindrical_velocity_dispersion_vector( + particle_weights: unyt.unyt_array, + particle_cylindrical_velocities: unyt.unyt_array, +) -> unyt.unyt_array: + """ + Compute the velocity dispersion along the radial, azimuthal and vertical + directions for the input particles and their specified weights. + + Parameters: + - particle_weights: unyt.unyt_array + Weight assigned to each particle. + - particle_cylindrical_velocities: unyt.unyt_array + Velocity of the particles in a cylindrical coordinate system. + + Returns a 3 element vector containing [sigma_r, sigma_phi, sigma_z]. + """ + + # This implementation of standard deviation is more numerically stable than using - ^2 + mean_velocity = (particle_weights[:, None] * particle_cylindrical_velocities).sum(axis=0) + squared_velocity_dispersion = ( + particle_weights[:, None] * (particle_cylindrical_velocities - mean_velocity) ** 2 + ).sum(axis=0) + + return np.sqrt(squared_velocity_dispersion) + +def get_cylindrical_velocity_dispersion_vector_mass_weighted( + particle_masses: unyt.unyt_array, + particle_cylindrical_velocities: unyt.unyt_array, +) -> unyt.unyt_array: + """ + Compute the mass-weighted velocity dispersion along the radial, azimuthal and vertical + directions for the input particles. + + Parameters: + - particle_masses: unyt.unyt_array + Mass of each particle. + - particle_cylindrical_velocities: unyt.unyt_array + Velocity of each particle in a cylindrical coordinate system. + + Returns a 3 element vector containing [sigma_r, sigma_phi, sigma_z]. + """ + mass_weights = particle_masses / particle_masses.sum() + return get_weighted_cylindrical_velocity_dispersion_vector(mass_weights, particle_cylindrical_velocities) def get_angular_momentum( mass: unyt.unyt_array, From 0e4ff31d4681fa81d90965108c3bf0c70d6628dd Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Wed, 1 Oct 2025 10:37:42 +0100 Subject: [PATCH 05/23] Define cylindrical dispersion quantities for aperture properties --- .../particle_selection/aperture_properties.py | 32 +++++++++++++++++++ parameter_files/COLIBRE_HYBRID.yml | 3 ++ parameter_files/COLIBRE_THERMAL.yml | 3 ++ 3 files changed, 38 insertions(+) diff --git a/SOAP/particle_selection/aperture_properties.py b/SOAP/particle_selection/aperture_properties.py index 04f9c082..6f16003d 100644 --- a/SOAP/particle_selection/aperture_properties.py +++ b/SOAP/particle_selection/aperture_properties.py @@ -148,6 +148,7 @@ def MetalFracStar(self): from SOAP.property_calculation.kinematic_properties import ( get_velocity_dispersion_matrix, get_rotation_velocity_mass_weighted, + get_cylindrical_velocity_dispersion_vector_mass_weighted, get_angular_momentum, get_angular_momentum_and_kappa_corot_mass_weighted, get_angular_momentum_and_kappa_corot_luminosity_weighted, @@ -1493,6 +1494,34 @@ def StellarRotationalVelocity(self) -> unyt.unyt_array: return None return get_rotation_velocity_mass_weighted(self.mass_star, self.star_cylindrical_velocities[:,1]) + @lazy_property + def StellarCylindricalVelocityDispersionVector(self) -> unyt.unyt_array: + if (self.Nstar < 2) or (np.sum(self.Lstar) == 0): + return None + return get_cylindrical_velocity_dispersion_vector_mass_weighted(self.mass_star, self.star_cylindrical_velocities) + + @lazy_property + def StellarCylindricalVelocityDispersion(self) -> unyt.unyt_array: + if self.StellarCylindricalVelocityDispersionVector is None: + return None + return np.sqrt( + (self.StellarCylindricalVelocityDispersionVector**2).sum() / 3 + ) + + @lazy_property + def StellarCylindricalVelocityDispersionVertical(self) -> unyt.unyt_array: + if self.StellarCylindricalVelocityDispersionVector is None: + return None + return self.StellarCylindricalVelocityDispersionVector[2] + + @lazy_property + def StellarCylindricalVelocityDispersionDiscPlane(self) -> unyt.unyt_array: + if self.StellarCylindricalVelocityDispersionVector is None: + return None + return np.sqrt( + (self.StellarCylindricalVelocityDispersionVector[:2]**2).sum() + ) + @lazy_property def KineticEnergyStars(self) -> unyt.unyt_quantity: """ @@ -3628,6 +3657,9 @@ class ApertureProperties(HaloProperty): "veldisp_matrix_gas": False, "veldisp_matrix_dm": False, "veldisp_matrix_star": False, + "StellarCylindricalVelocityDispersion": False, + "StellarCylindricalVelocityDispersionVertical": False, + "StellarCylindricalVelocityDispersionDiscPlane": False, "StellarRotationalVelocity": False, "KineticEnergyGas": False, "KineticEnergyStars": False, diff --git a/parameter_files/COLIBRE_HYBRID.yml b/parameter_files/COLIBRE_HYBRID.yml index 0662c16d..f94687a6 100644 --- a/parameter_files/COLIBRE_HYBRID.yml +++ b/parameter_files/COLIBRE_HYBRID.yml @@ -48,6 +48,9 @@ ApertureProperties: DarkMatterMass: true DarkMatterCentreOfMass: true DarkMatterVelocityDispersionMatrix: general + StellarCylindricalVelocityDispersion: false + StellarCylindricalVelocityDispersionVertical: false + StellarCylindricalVelocityDispersionDiscPlane: false DiffuseCarbonMass: snapshot: true snipshot: false diff --git a/parameter_files/COLIBRE_THERMAL.yml b/parameter_files/COLIBRE_THERMAL.yml index 4bba494a..21761c68 100644 --- a/parameter_files/COLIBRE_THERMAL.yml +++ b/parameter_files/COLIBRE_THERMAL.yml @@ -236,6 +236,9 @@ ApertureProperties: StellarMassFractionInMetals: true StellarMassFractionInOxygen: true StellarVelocityDispersionMatrix: general + StellarCylindricalVelocityDispersion: false + StellarCylindricalVelocityDispersionVertical: false + StellarCylindricalVelocityDispersionDiscPlane: false StellarRotationalVelocity: false TotalMass: true TotalSNIaRate: true From cdeb1e73106381dde47310e0c9c7ecf9d6599ab1 Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Wed, 1 Oct 2025 10:37:56 +0100 Subject: [PATCH 06/23] Add missing property declaration --- SOAP/particle_selection/aperture_properties.py | 1 + 1 file changed, 1 insertion(+) diff --git a/SOAP/particle_selection/aperture_properties.py b/SOAP/particle_selection/aperture_properties.py index 6f16003d..6ae698fc 100644 --- a/SOAP/particle_selection/aperture_properties.py +++ b/SOAP/particle_selection/aperture_properties.py @@ -3657,6 +3657,7 @@ class ApertureProperties(HaloProperty): "veldisp_matrix_gas": False, "veldisp_matrix_dm": False, "veldisp_matrix_star": False, + "StellarRotationalVelocity": False, "StellarCylindricalVelocityDispersion": False, "StellarCylindricalVelocityDispersionVertical": False, "StellarCylindricalVelocityDispersionDiscPlane": False, From c5f37d47e632b48010419fe713a0a8188bc22c09 Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Wed, 1 Oct 2025 10:48:59 +0100 Subject: [PATCH 07/23] Define luminosity-weighted kinematic properties Still need to think of how to handle different angular momentum vectors. --- .../particle_selection/aperture_properties.py | 4 +- SOAP/particle_selection/subhalo_properties.py | 2 + .../kinematic_properties.py | 53 +++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/SOAP/particle_selection/aperture_properties.py b/SOAP/particle_selection/aperture_properties.py index 6ae698fc..98d6335c 100644 --- a/SOAP/particle_selection/aperture_properties.py +++ b/SOAP/particle_selection/aperture_properties.py @@ -147,8 +147,10 @@ def MetalFracStar(self): ) from SOAP.property_calculation.kinematic_properties import ( get_velocity_dispersion_matrix, - get_rotation_velocity_mass_weighted, get_cylindrical_velocity_dispersion_vector_mass_weighted, + get_cylindrical_velocity_dispersion_vector_luminosity_weighted, + get_rotation_velocity_mass_weighted, + get_rotation_velocity_luminosity_weighted, get_angular_momentum, get_angular_momentum_and_kappa_corot_mass_weighted, get_angular_momentum_and_kappa_corot_luminosity_weighted, diff --git a/SOAP/particle_selection/subhalo_properties.py b/SOAP/particle_selection/subhalo_properties.py index d6ee10cb..d568ee64 100644 --- a/SOAP/particle_selection/subhalo_properties.py +++ b/SOAP/particle_selection/subhalo_properties.py @@ -34,7 +34,9 @@ get_vmax, get_velocity_dispersion_matrix, get_cylindrical_velocity_dispersion_vector_mass_weighted, + get_cylindrical_velocity_dispersion_vector_luminosity_weighted, get_rotation_velocity_mass_weighted, + get_rotation_velocity_luminosity_weighted, ) from SOAP.property_calculation.inertia_tensors import ( get_inertia_tensor_mass_weighted, diff --git a/SOAP/property_calculation/kinematic_properties.py b/SOAP/property_calculation/kinematic_properties.py index da11b3de..816990f2 100644 --- a/SOAP/property_calculation/kinematic_properties.py +++ b/SOAP/property_calculation/kinematic_properties.py @@ -46,6 +46,31 @@ def get_rotation_velocity_mass_weighted(particle_masses, particle_azimuthal_velo mass_weights = particle_masses / particle_masses.sum() return get_weighted_rotation_velocity(mass_weights, particle_azimuthal_velocities) +def get_rotation_velocity_luminosity_weighted(particle_luminosities: unyt.unyt_array, + particle_azimuthal_velocities: unyt.unyt_array) -> unyt.unyt_array: + """ + Return the luminosity-weighted average azimuthal velocity of a particle distribution, for each + provided luminosity band. + + Parameters: + - particle_luminosities: unyt.unyt_array + Luminosity of each particle in the provided bands. + - particle_azimuthal_velocities: unyt.unyt_array + Azimuthal velocity of each particle. + + Returns: + - Luminosity-weighted average of the azimuthal velocity of particles, with a value for each band. + """ + + number_luminosity_bands = particle_luminosities.shape[1] + rotation_velocities = np.zeros(number_luminosity_bands.shape[0]) * particle_azimuthal_velocities.units + + for i_band, particle_luminosities_i_band in enumerate(particle_luminosities.T): + luminosity_weights = particle_luminosities_i_band / particle_luminosities_i_band.sum() + rotation_velocities[i_band] = get_weighted_rotation_velocity(luminosity_weights, particle_azimuthal_velocities) + + return rotation_velocities + def get_velocity_dispersion_matrix( mass_fraction: unyt.unyt_array, velocity: unyt.unyt_array, @@ -128,6 +153,34 @@ def get_cylindrical_velocity_dispersion_vector_mass_weighted( mass_weights = particle_masses / particle_masses.sum() return get_weighted_cylindrical_velocity_dispersion_vector(mass_weights, particle_cylindrical_velocities) +def get_cylindrical_velocity_dispersion_vector_luminosity_weighted( + particle_luminosities: unyt.unyt_array, + particle_cylindrical_velocities: unyt.unyt_array, +) -> unyt.unyt_array: + """ + Compute the luminosity-weighted velocity dispersion along the radial, azimuthal and vertical + directions for the input particles. We return a vector for each luminosity band. + + Parameters: + - particle_luminosities: unyt.unyt_array + Luminosity of each particle in different luminosity bands. + - particle_cylindrical_velocities: unyt.unyt_array + Velocity of each particle in a cylindrical coordinate system. + + Returns a 3 element vector for each luminosity band, which contains [sigma_r, sigma_phi, sigma_z]. + The velocity dispersion vectors for each band are appended to the same vector, hence the shape is + number_luminosity_bands * 3. + """ + + number_luminosity_bands = particle_luminosities.shape[1] + velocity_dispersion_vectors = np.zeros(number_luminosity_bands.shape[0] * 3) * particle_cylindrical_velocities.units + + for i_band, particle_luminosities_i_band in enumerate(particle_luminosities.T): + luminosity_weights = particle_luminosities_i_band / particle_luminosities_i_band.sum() + velocity_dispersion_vectors[i_band*3:(i_band + 1)*3] = get_weighted_cylindrical_velocity_dispersion_vector(luminosity_weights, particle_cylindrical_velocities) + + return velocity_dispersion_vectors + def get_angular_momentum( mass: unyt.unyt_array, position: unyt.unyt_array, From 3884a430fa8fc89f3c7c70de26a35455b63c3719 Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Wed, 1 Oct 2025 11:26:30 +0100 Subject: [PATCH 08/23] Change calculate_cylindrical_velocities to take reference pos and vel --- SOAP/particle_selection/aperture_properties.py | 16 +++++++--------- SOAP/particle_selection/subhalo_properties.py | 18 ++++++++---------- .../cylindrical_coordinates.py | 10 +++++++++- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/SOAP/particle_selection/aperture_properties.py b/SOAP/particle_selection/aperture_properties.py index 98d6335c..1f6112dc 100644 --- a/SOAP/particle_selection/aperture_properties.py +++ b/SOAP/particle_selection/aperture_properties.py @@ -1472,19 +1472,17 @@ def star_cylindrical_velocities(self) -> unyt.unyt_array: if np.sum(self.Lstar) == 0: return None - # Calculate the position of the stars relative to their CoM - pos = self.pos_star - (self.star_mass_fraction[:, None] * self.pos_star).sum( - axis=0 - ) - - # Calculate relative velocity of stars - vrel = self.vel_star - self.vcom[None, :] + # Calculate the position of the stars relative to their CoM. We do not + # use self.com because it is offset and we want to weight differently + com = (self.star_mass_fraction[:, None] * self.pos_star).sum(axis=0) # Get velocities in cylindrical coordinates return calculate_cylindrical_velocities( - pos, - vrel, + self.pos_star, + self.vel_star, self.Lstar, + com, + self.vcom ) @lazy_property diff --git a/SOAP/particle_selection/subhalo_properties.py b/SOAP/particle_selection/subhalo_properties.py index d568ee64..e97fa875 100644 --- a/SOAP/particle_selection/subhalo_properties.py +++ b/SOAP/particle_selection/subhalo_properties.py @@ -1407,19 +1407,17 @@ def star_cylindrical_velocities(self) -> unyt.unyt_array: if np.sum(self.Lstar) == 0: return None - # Calculate the position of the stars relative to their CoM - pos = self.pos_star - (self.star_mass_fraction[:, None] * self.pos_star).sum( - axis=0 - ) - - # Calculate relative velocity of stars - vrel = self.vel_star - self.vcom[None, :] + # Calculate the position of the stars relative to their CoM. We do not + # use self.com because it is offset and we want to weight differently + com = (self.star_mass_fraction[:, None] * self.pos_star).sum(axis=0) # Get velocities in cylindrical coordinates return calculate_cylindrical_velocities( - pos, - vrel, + self.pos_star, + self.vel_star, self.Lstar, + com, + self.vcom ) @lazy_property @@ -2301,10 +2299,10 @@ class SubhaloProperties(HaloProperty): "Lgas", "Ldm", "Lstar", + "StellarRotationalVelocity", "StellarCylindricalVelocityDispersion", "StellarCylindricalVelocityDispersionVertical", "StellarCylindricalVelocityDispersionDiscPlane", - "StellarRotationalVelocity", "kappa_corot_gas", "kappa_corot_star", "Lbaryons", diff --git a/SOAP/property_calculation/cylindrical_coordinates.py b/SOAP/property_calculation/cylindrical_coordinates.py index 223ac266..1102b415 100644 --- a/SOAP/property_calculation/cylindrical_coordinates.py +++ b/SOAP/property_calculation/cylindrical_coordinates.py @@ -42,7 +42,7 @@ def build_rotation_matrix(z_target): return R -def calculate_cylindrical_velocities(positions, velocities, z_target): +def calculate_cylindrical_velocities(positions, velocities, z_target, reference_position = None, reference_velocity = None): """ Convert 3D Cartesian velocities to cylindrical coordinates (v_r, v_phi, v_z), after rotating the system such that the z-axis aligns with `z_target`. @@ -51,11 +51,19 @@ def calculate_cylindrical_velocities(positions, velocities, z_target): positions: (N, 3) array of particle positions in the original Cartesian frame. velocities: (N, 3) array of particle velocities in the original Cartesian frame. z_target: A 3-element vector indicating the new z-axis direction. + reference_position: (3,) array with a reference position on which to centre the Cartesian coordinate system. + reference_velocity: (3,) array with a reference velocity on which to centre the Cartesian reference frame. Returns: cyl_velocities: (N, 3) array of velocities in cylindrical coordinates: [v_r, v_phi, v_z] for each particle. """ + + if reference_position is not None: + positions -= reference_position + if reference_velocity is not None: + velocities -= reference_velocity + R = build_rotation_matrix(z_target) # Rotate positions and velocities into new frame From 99041a8b19e4879d0ff4a67fc6085b91998337da Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Wed, 1 Oct 2025 11:36:13 +0100 Subject: [PATCH 09/23] Define cylindrical velocity calculation on a per-luminosity-band basis --- SOAP/particle_selection/subhalo_properties.py | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/SOAP/particle_selection/subhalo_properties.py b/SOAP/particle_selection/subhalo_properties.py index e97fa875..08b7d9b1 100644 --- a/SOAP/particle_selection/subhalo_properties.py +++ b/SOAP/particle_selection/subhalo_properties.py @@ -1393,7 +1393,7 @@ def star_mass_fraction(self) -> unyt.unyt_array: @lazy_property def star_cylindrical_velocities(self) -> unyt.unyt_array: """ - Calculate the velocities of the star particles in cyclindrical + Calculate the velocities of the star particles in cylindrical coordinates, where the axes are centred on the stellar CoM, and the z axis is aligned with the stellar angular momentum. """ @@ -1455,6 +1455,42 @@ def StellarCylindricalVelocityDispersionDiscPlane(self) -> unyt.unyt_array: ) @lazy_property + def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: + """ + Calculate the velocities of the star particles in cylindrical + coordinates, where the origin and reference frame are centred on the + stellar centre of light in each band. The z axis is aligned with the + stellar angular momentum obtained from each band. + """ + + # We need at least 2 particles to have an angular momentum vector + if self.Nstar < 2: + return None + + # This can happen if we have particles on top of each other + # or with the same velocity + if np.sum(self.internal_Lstar_luminosity_weighted) == 0: + return None + + # We iterate over bands to use their own reference vector and luminosity- + # weighted centre of mass phase space coordinates. + cylindrical_velocities = np.zeros(self.stellar_luminosities.shape) * self.vel_star.units + for i_band, particle_luminosities_i_band in enumerate(self.stellar_luminosities.T): + + luminosity_weights = particle_luminosities_i_band / particle_luminosities_i_band.sum() + centre_of_light_position = (luminosity_weights[:, None] * self.pos_star).sum(axis=0) + centre_of_light_velocity = (luminosity_weights[:, None] * self.vel_star).sum(axis=0) + + cylindrical_velocities[i_band] = calculate_cylindrical_velocities( + self.pos_star, + self.vel_star, + self.internal_Lstar_luminosity_weighted[i_band], + centre_of_light_position, + centre_of_light_velocity + ) + + return cylindrical_velocities + @lazy_property def vcom_star(self) -> unyt.unyt_array: """ Centre of mass velocity of star particles in the subhalo. From 0b9d09429c643b3aa5347fdb3e7fd157c08e7c37 Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Wed, 1 Oct 2025 13:14:27 +0100 Subject: [PATCH 10/23] Implementation of luminosity-weighted rotational velocity Testing pending. --- SOAP/particle_selection/subhalo_properties.py | 7 +++++++ SOAP/property_calculation/kinematic_properties.py | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/SOAP/particle_selection/subhalo_properties.py b/SOAP/particle_selection/subhalo_properties.py index 08b7d9b1..823b1922 100644 --- a/SOAP/particle_selection/subhalo_properties.py +++ b/SOAP/particle_selection/subhalo_properties.py @@ -1490,6 +1490,13 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: ) return cylindrical_velocities + + @lazy_property + def StellarRotationalVelocityLuminosityWeighted(self) -> unyt.unyt_array: + if (self.Nstar < 2) or (np.sum(self.Lstar) == 0): + return None + return get_rotation_velocity_luminosity_weighted(self.stellar_luminosities, self.star_cylindrical_velocities_luminosity_weighted[:,:,1]) + @lazy_property def vcom_star(self) -> unyt.unyt_array: """ diff --git a/SOAP/property_calculation/kinematic_properties.py b/SOAP/property_calculation/kinematic_properties.py index 816990f2..e4f91322 100644 --- a/SOAP/property_calculation/kinematic_properties.py +++ b/SOAP/property_calculation/kinematic_properties.py @@ -56,7 +56,7 @@ def get_rotation_velocity_luminosity_weighted(particle_luminosities: unyt.unyt_a - particle_luminosities: unyt.unyt_array Luminosity of each particle in the provided bands. - particle_azimuthal_velocities: unyt.unyt_array - Azimuthal velocity of each particle. + Azimuthal velocity of each particle, pre-computed for each luminosity band. Returns: - Luminosity-weighted average of the azimuthal velocity of particles, with a value for each band. @@ -65,9 +65,9 @@ def get_rotation_velocity_luminosity_weighted(particle_luminosities: unyt.unyt_a number_luminosity_bands = particle_luminosities.shape[1] rotation_velocities = np.zeros(number_luminosity_bands.shape[0]) * particle_azimuthal_velocities.units - for i_band, particle_luminosities_i_band in enumerate(particle_luminosities.T): + for i_band, (particle_luminosities_i_band, particle_azimuthal_velocities_i_band) in enumerate(zip(particle_luminosities.T, particle_azimuthal_velocities.T)): luminosity_weights = particle_luminosities_i_band / particle_luminosities_i_band.sum() - rotation_velocities[i_band] = get_weighted_rotation_velocity(luminosity_weights, particle_azimuthal_velocities) + rotation_velocities[i_band] = get_weighted_rotation_velocity(luminosity_weights, particle_azimuthal_velocities_i_band) return rotation_velocities From dbe36166690f13587e08dee39cff82ebc515c557 Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Wed, 1 Oct 2025 13:24:53 +0100 Subject: [PATCH 11/23] Implement luminosity-weighted cylindrical dispersion routines. Tests to be done --- SOAP/particle_selection/subhalo_properties.py | 28 +++++++++++++++++++ .../kinematic_properties.py | 11 ++++---- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/SOAP/particle_selection/subhalo_properties.py b/SOAP/particle_selection/subhalo_properties.py index 823b1922..c34ae6d4 100644 --- a/SOAP/particle_selection/subhalo_properties.py +++ b/SOAP/particle_selection/subhalo_properties.py @@ -1497,6 +1497,34 @@ def StellarRotationalVelocityLuminosityWeighted(self) -> unyt.unyt_array: return None return get_rotation_velocity_luminosity_weighted(self.stellar_luminosities, self.star_cylindrical_velocities_luminosity_weighted[:,:,1]) + @lazy_property + def StellarCylindricalVelocityDispersionVectorLuminosityWeighted(self) -> unyt.unyt_array: + if (self.Nstar < 2) or (np.sum(self.Lstar) == 0): + return None + return get_cylindrical_velocity_dispersion_vector_luminosity_weighted(self.stellar_luminosities, self.star_cylindrical_velocities_luminosity_weighted) + + @lazy_property + def StellarCylindricalVelocityDispersionLuminosityWeighted(self) -> unyt.unyt_array: + if self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted is None: + return None + return np.sqrt( + (self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted**2).sum(axis=1) / 3 + ) + + @lazy_property + def StellarCylindricalVelocityDispersionVerticalLuminosityWeighted(self) -> unyt.unyt_array: + if self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted is None: + return None + return self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted[:,2] + + @lazy_property + def StellarCylindricalVelocityDispersionDiscPlaneLuminosityWeighted(self) -> unyt.unyt_array: + if self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted is None: + return None + return np.sqrt( + (self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted[:,:2]**2).sum(axis=1) + ) + @lazy_property def vcom_star(self) -> unyt.unyt_array: """ diff --git a/SOAP/property_calculation/kinematic_properties.py b/SOAP/property_calculation/kinematic_properties.py index e4f91322..cbdabc22 100644 --- a/SOAP/property_calculation/kinematic_properties.py +++ b/SOAP/property_calculation/kinematic_properties.py @@ -165,19 +165,20 @@ def get_cylindrical_velocity_dispersion_vector_luminosity_weighted( - particle_luminosities: unyt.unyt_array Luminosity of each particle in different luminosity bands. - particle_cylindrical_velocities: unyt.unyt_array - Velocity of each particle in a cylindrical coordinate system. + Velocity of each particle in a cylindrical coordinate system, which varies between different + luminosity bands. Returns a 3 element vector for each luminosity band, which contains [sigma_r, sigma_phi, sigma_z]. The velocity dispersion vectors for each band are appended to the same vector, hence the shape is - number_luminosity_bands * 3. + (number_luminosity_bands, 3). """ number_luminosity_bands = particle_luminosities.shape[1] - velocity_dispersion_vectors = np.zeros(number_luminosity_bands.shape[0] * 3) * particle_cylindrical_velocities.units + velocity_dispersion_vectors = np.zeros((number_luminosity_bands.shape[0], 3)) * particle_cylindrical_velocities.units - for i_band, particle_luminosities_i_band in enumerate(particle_luminosities.T): + for i_band, (particle_luminosities_i_band, particle_cylindrical_velocities_i_band) in enumerate(zip(particle_luminosities.T, particle_cylindrical_velocities.T)): luminosity_weights = particle_luminosities_i_band / particle_luminosities_i_band.sum() - velocity_dispersion_vectors[i_band*3:(i_band + 1)*3] = get_weighted_cylindrical_velocity_dispersion_vector(luminosity_weights, particle_cylindrical_velocities) + velocity_dispersion_vectors[i_band] = get_weighted_cylindrical_velocity_dispersion_vector(luminosity_weights, particle_cylindrical_velocities_i_band) return velocity_dispersion_vectors From 7331ff354931888fd31a1d628d57f68d8a0da7ac Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Wed, 1 Oct 2025 13:33:34 +0100 Subject: [PATCH 12/23] Define luminosity weighted kinematics for aperture properties --- .../particle_selection/aperture_properties.py | 76 ++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/SOAP/particle_selection/aperture_properties.py b/SOAP/particle_selection/aperture_properties.py index 1f6112dc..2cd64b10 100644 --- a/SOAP/particle_selection/aperture_properties.py +++ b/SOAP/particle_selection/aperture_properties.py @@ -1522,6 +1522,77 @@ def StellarCylindricalVelocityDispersionDiscPlane(self) -> unyt.unyt_array: (self.StellarCylindricalVelocityDispersionVector[:2]**2).sum() ) + @lazy_property + def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: + """ + Calculate the velocities of the star particles in cylindrical + coordinates, where the origin and reference frame are centred on the + stellar centre of light in each band. The z axis is aligned with the + stellar angular momentum obtained from each band. + """ + + # We need at least 2 particles to have an angular momentum vector + if self.Nstar < 2: + return None + + # This can happen if we have particles on top of each other + # or with the same velocity + if np.sum(self.internal_Lstar_luminosity_weighted) == 0: + return None + + # We iterate over bands to use their own reference vector and luminosity- + # weighted centre of mass phase space coordinates. + cylindrical_velocities = np.zeros(self.stellar_luminosities.shape) * self.vel_star.units + for i_band, particle_luminosities_i_band in enumerate(self.stellar_luminosities.T): + + luminosity_weights = particle_luminosities_i_band / particle_luminosities_i_band.sum() + centre_of_light_position = (luminosity_weights[:, None] * self.pos_star).sum(axis=0) + centre_of_light_velocity = (luminosity_weights[:, None] * self.vel_star).sum(axis=0) + + cylindrical_velocities[i_band] = calculate_cylindrical_velocities( + self.pos_star, + self.vel_star, + self.internal_Lstar_luminosity_weighted[i_band], + centre_of_light_position, + centre_of_light_velocity + ) + + return cylindrical_velocities + + @lazy_property + def StellarRotationalVelocityLuminosityWeighted(self) -> unyt.unyt_array: + if (self.Nstar < 2) or (np.sum(self.Lstar) == 0): + return None + return get_rotation_velocity_luminosity_weighted(self.stellar_luminosities, self.star_cylindrical_velocities_luminosity_weighted[:,:,1]) + + @lazy_property + def StellarCylindricalVelocityDispersionVectorLuminosityWeighted(self) -> unyt.unyt_array: + if (self.Nstar < 2) or (np.sum(self.Lstar) == 0): + return None + return get_cylindrical_velocity_dispersion_vector_luminosity_weighted(self.stellar_luminosities, self.star_cylindrical_velocities_luminosity_weighted) + + @lazy_property + def StellarCylindricalVelocityDispersionLuminosityWeighted(self) -> unyt.unyt_array: + if self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted is None: + return None + return np.sqrt( + (self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted**2).sum(axis=1) / 3 + ) + + @lazy_property + def StellarCylindricalVelocityDispersionVerticalLuminosityWeighted(self) -> unyt.unyt_array: + if self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted is None: + return None + return self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted[:,2] + + @lazy_property + def StellarCylindricalVelocityDispersionDiscPlaneLuminosityWeighted(self) -> unyt.unyt_array: + if self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted is None: + return None + return np.sqrt( + (self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted[:,:2]**2).sum(axis=1) + ) + @lazy_property def KineticEnergyStars(self) -> unyt.unyt_quantity: """ @@ -3661,7 +3732,10 @@ class ApertureProperties(HaloProperty): "StellarCylindricalVelocityDispersion": False, "StellarCylindricalVelocityDispersionVertical": False, "StellarCylindricalVelocityDispersionDiscPlane": False, - "StellarRotationalVelocity": False, + "StellarRotationalVelocityLuminosityWeighted": False, + "StellarCylindricalVelocityDispersionLuminosityWeighted": False, + "StellarCylindricalVelocityDispersionVerticalLuminosityWeighted": False, + "StellarCylindricalVelocityDispersionDiscPlaneLuminosityWeighted": False, "KineticEnergyGas": False, "KineticEnergyStars": False, "Mgas_SF": False, From 9e3f9899e5f302b63d283be410a8b7cb447bbda9 Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Wed, 1 Oct 2025 13:57:48 +0100 Subject: [PATCH 13/23] Add new properties to parameter file --- parameter_files/COLIBRE_HYBRID.yml | 14 +++++++++++--- parameter_files/COLIBRE_THERMAL.yml | 8 ++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/parameter_files/COLIBRE_HYBRID.yml b/parameter_files/COLIBRE_HYBRID.yml index f94687a6..0f9b3f6e 100644 --- a/parameter_files/COLIBRE_HYBRID.yml +++ b/parameter_files/COLIBRE_HYBRID.yml @@ -48,9 +48,6 @@ ApertureProperties: DarkMatterMass: true DarkMatterCentreOfMass: true DarkMatterVelocityDispersionMatrix: general - StellarCylindricalVelocityDispersion: false - StellarCylindricalVelocityDispersionVertical: false - StellarCylindricalVelocityDispersionDiscPlane: false DiffuseCarbonMass: snapshot: true snipshot: false @@ -240,6 +237,13 @@ ApertureProperties: StellarMassFractionInOxygen: true StellarVelocityDispersionMatrix: general StellarRotationalVelocity: false + StellarCylindricalVelocityDispersion: false + StellarCylindricalVelocityDispersionVertical: false + StellarCylindricalVelocityDispersionDiscPlane: false + StellarRotationalVelocityLuminosityWeighted: false + StellarCylindricalVelocityDispersionLuminosityWeighted: false + StellarCylindricalVelocityDispersionVerticalLuminosityWeighted: false + StellarCylindricalVelocityDispersionDiscPlaneLuminosityWeighted: false TotalMass: true TotalSNIaRate: true GasMassInColdDenseDiffuseMetals: @@ -734,6 +738,10 @@ SubhaloProperties: StellarCylindricalVelocityDispersion: general StellarCylindricalVelocityDispersionVertical: false StellarCylindricalVelocityDispersionDiscPlane: false + StellarRotationalVelocityLuminosityWeighted: false + StellarCylindricalVelocityDispersionLuminosityWeighted: false + StellarCylindricalVelocityDispersionVerticalLuminosityWeighted: false + StellarCylindricalVelocityDispersionDiscPlaneLuminosityWeighted: false aliases: PartType0/LastSNIIKineticFeedbackDensities: PartType0/DensitiesAtLastSupernovaEvent PartType0/LastSNIIThermalFeedbackDensities: PartType0/DensitiesAtLastSupernovaEvent diff --git a/parameter_files/COLIBRE_THERMAL.yml b/parameter_files/COLIBRE_THERMAL.yml index 21761c68..403267a5 100644 --- a/parameter_files/COLIBRE_THERMAL.yml +++ b/parameter_files/COLIBRE_THERMAL.yml @@ -239,7 +239,11 @@ ApertureProperties: StellarCylindricalVelocityDispersion: false StellarCylindricalVelocityDispersionVertical: false StellarCylindricalVelocityDispersionDiscPlane: false + StellarCylindricalVelocityDispersionLuminosityWeighted: false + StellarCylindricalVelocityDispersionVerticalLuminosityWeighted: false + StellarCylindricalVelocityDispersionDiscPlaneLuminosityWeighted: false StellarRotationalVelocity: false + StellarRotationalVelocityLuminosityWeighted: false TotalMass: true TotalSNIaRate: true GasMassInColdDenseDiffuseMetals: @@ -734,6 +738,10 @@ SubhaloProperties: StellarCylindricalVelocityDispersion: general StellarCylindricalVelocityDispersionVertical: false StellarCylindricalVelocityDispersionDiscPlane: false + StellarRotationalVelocityLuminosityWeighted: false + StellarCylindricalVelocityDispersionLuminosityWeighted: false + StellarCylindricalVelocityDispersionVerticalLuminosityWeighted: false + StellarCylindricalVelocityDispersionDiscPlaneLuminosityWeighted: false aliases: PartType0/LastSNIIKineticFeedbackDensities: PartType0/DensitiesAtLastSupernovaEvent PartType0/LastSNIIThermalFeedbackDensities: PartType0/DensitiesAtLastSupernovaEvent From 471365e224cde4c3d6b3fcbfe79da293a76b17af Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Thu, 2 Oct 2025 10:44:20 +0100 Subject: [PATCH 14/23] Add new properties to property table --- SOAP/property_table.py | 64 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/SOAP/property_table.py b/SOAP/property_table.py index 9666b4ac..ecb00c8a 100644 --- a/SOAP/property_table.py +++ b/SOAP/property_table.py @@ -1716,6 +1716,70 @@ class PropertyTable: output_physical=True, a_scale_exponent=0, ), + "StellarRotationalVelocityLuminosityWeighted": Property( + name="StellarRotationalVelocityLuminosityWeighted", + shape=9, # GAMA bands + dtype=np.float32, + unit="snap_length/snap_time", + description="Luminosity-weighted mean rotational velocity of the stars, in a cylindrical coordinate system where the axes are centred on the stellar CoM, and the z axis is aligned with the stellar angular momentum.", + lossy_compression_filter="FMantissa9", + dmo_property=False, + particle_properties=[ + "PartType4/Coordinates", + "PartType4/Luminosities", + "PartType4/Velocities", + ], + output_physical=True, + a_scale_exponent=0, + ), + "StellarCylindricalVelocityDispersionLuminosityWeighted": Property( + name="StellarCylindricalVelocityDispersionLuminosityWeighted", + shape=9, # GAMA bands + dtype=np.float32, + unit="snap_length/snap_time", + description="One-dimensional luminosity-weighted velocity dispersion of the stars computed in a cylindrical coordinate system where the axes are centred on the stellar CoM, and the z axis is aligned with the stellar angular momentum.", + lossy_compression_filter="FMantissa9", + dmo_property=False, + particle_properties=[ + "PartType4/Coordinates", + "PartType4/Luminosities", + "PartType4/Velocities", + ], + output_physical=True, + a_scale_exponent=0, + ), + "StellarCylindricalVelocityDispersionVerticalLuminosityWeighted": Property( + name="StellarCylindricalVelocityDispersionVerticalLuminosityWeighted", + shape=9, # GAMA bands + dtype=np.float32, + unit="snap_length/snap_time", + description="Velocity dispersion perpendicular to the orbital plane of the stars, luminosity-weighted and computed in a cylindrical coordinate system where the axes are centred on the stellar CoM, and the z axis is aligned with the stellar angular momentum.", + lossy_compression_filter="FMantissa9", + dmo_property=False, + particle_properties=[ + "PartType4/Coordinates", + "PartType4/Luminosities", + "PartType4/Velocities", + ], + output_physical=True, + a_scale_exponent=0, + ), + "StellarCylindricalVelocityDispersionDiscPlaneLuminosityWeighted": Property( + name="StellarCylindricalVelocityDispersionDiscPlaneLuminosityWeighted", + shape=9, # GAMA bands + dtype=np.float32, + unit="snap_length/snap_time", + description="Total velocity dispersion in the orbital plane of the stars, luminosity-weighted and computed in a cylindrical coordinate system where the axes are centred on the stellar CoM, and the z axis is aligned with the stellar angular momentum.", + lossy_compression_filter="FMantissa9", + dmo_property=False, + particle_properties=[ + "PartType4/Coordinates", + "PartType4/Luminosities", + "PartType4/Velocities", + ], + output_physical=True, + a_scale_exponent=0, + ), "MaximumStellarBirthDensity": Property( name="MaximumStellarBirthDensity", shape=1, From df68731587a0cea4134d6c3b5d2161183b54b7a5 Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Thu, 2 Oct 2025 10:45:01 +0100 Subject: [PATCH 15/23] Incorrect function call --- SOAP/particle_selection/aperture_properties.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SOAP/particle_selection/aperture_properties.py b/SOAP/particle_selection/aperture_properties.py index 2cd64b10..66d85ede 100644 --- a/SOAP/particle_selection/aperture_properties.py +++ b/SOAP/particle_selection/aperture_properties.py @@ -1537,7 +1537,7 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: # This can happen if we have particles on top of each other # or with the same velocity - if np.sum(self.internal_Lstar_luminosity_weighted) == 0: + if np.sum(self.Lstar_luminosity_weighted) == 0: return None # We iterate over bands to use their own reference vector and luminosity- @@ -1552,7 +1552,7 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: cylindrical_velocities[i_band] = calculate_cylindrical_velocities( self.pos_star, self.vel_star, - self.internal_Lstar_luminosity_weighted[i_band], + self.Lstar_luminosity_weighted[i_band], centre_of_light_position, centre_of_light_velocity ) From 069a2c711ec84e11de9f028fb4499b5c8e2aa352 Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Thu, 2 Oct 2025 10:47:52 +0100 Subject: [PATCH 16/23] Declare properties for bound subhalo --- SOAP/particle_selection/subhalo_properties.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/SOAP/particle_selection/subhalo_properties.py b/SOAP/particle_selection/subhalo_properties.py index c34ae6d4..db6e2f05 100644 --- a/SOAP/particle_selection/subhalo_properties.py +++ b/SOAP/particle_selection/subhalo_properties.py @@ -1469,7 +1469,7 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: # This can happen if we have particles on top of each other # or with the same velocity - if np.sum(self.internal_Lstar_luminosity_weighted) == 0: + if np.sum(self.Lstar_luminosity_weighted) == 0: return None # We iterate over bands to use their own reference vector and luminosity- @@ -1484,7 +1484,7 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: cylindrical_velocities[i_band] = calculate_cylindrical_velocities( self.pos_star, self.vel_star, - self.internal_Lstar_luminosity_weighted[i_band], + self.Lstar_luminosity_weighted[i_band], centre_of_light_position, centre_of_light_velocity ) @@ -2374,6 +2374,10 @@ class SubhaloProperties(HaloProperty): "StellarCylindricalVelocityDispersion", "StellarCylindricalVelocityDispersionVertical", "StellarCylindricalVelocityDispersionDiscPlane", + "StellarRotationalVelocityLuminosityWeighted", + "StellarCylindricalVelocityDispersionLuminosityWeighted", + "StellarCylindricalVelocityDispersionVerticalLuminosityWeighted", + "StellarCylindricalVelocityDispersionDiscPlaneLuminosityWeighted", "kappa_corot_gas", "kappa_corot_star", "Lbaryons", From 0c403d61ef3f3a48a6e51694f733e50c750a2e03 Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Thu, 2 Oct 2025 12:19:13 +0100 Subject: [PATCH 17/23] Fix indexing of arrays --- SOAP/particle_selection/aperture_properties.py | 4 ++-- SOAP/particle_selection/subhalo_properties.py | 4 ++-- SOAP/property_calculation/kinematic_properties.py | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/SOAP/particle_selection/aperture_properties.py b/SOAP/particle_selection/aperture_properties.py index 66d85ede..9cb9b8bb 100644 --- a/SOAP/particle_selection/aperture_properties.py +++ b/SOAP/particle_selection/aperture_properties.py @@ -1542,7 +1542,7 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: # We iterate over bands to use their own reference vector and luminosity- # weighted centre of mass phase space coordinates. - cylindrical_velocities = np.zeros(self.stellar_luminosities.shape) * self.vel_star.units + cylindrical_velocities = np.zeros((self.stellar_luminosities.shape[1], self.stellar_luminosities.shape[0], 3)) * self.vel_star.units for i_band, particle_luminosities_i_band in enumerate(self.stellar_luminosities.T): luminosity_weights = particle_luminosities_i_band / particle_luminosities_i_band.sum() @@ -1552,7 +1552,7 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: cylindrical_velocities[i_band] = calculate_cylindrical_velocities( self.pos_star, self.vel_star, - self.Lstar_luminosity_weighted[i_band], + self.Lstar_luminosity_weighted[i_band * 3: (1 + i_band) * 3], centre_of_light_position, centre_of_light_velocity ) diff --git a/SOAP/particle_selection/subhalo_properties.py b/SOAP/particle_selection/subhalo_properties.py index db6e2f05..45817c93 100644 --- a/SOAP/particle_selection/subhalo_properties.py +++ b/SOAP/particle_selection/subhalo_properties.py @@ -1474,7 +1474,7 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: # We iterate over bands to use their own reference vector and luminosity- # weighted centre of mass phase space coordinates. - cylindrical_velocities = np.zeros(self.stellar_luminosities.shape) * self.vel_star.units + cylindrical_velocities = np.zeros((self.stellar_luminosities.shape[1], self.stellar_luminosities.shape[0], 3)) * self.vel_star.units for i_band, particle_luminosities_i_band in enumerate(self.stellar_luminosities.T): luminosity_weights = particle_luminosities_i_band / particle_luminosities_i_band.sum() @@ -1484,7 +1484,7 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: cylindrical_velocities[i_band] = calculate_cylindrical_velocities( self.pos_star, self.vel_star, - self.Lstar_luminosity_weighted[i_band], + self.Lstar_luminosity_weighted[i_band * 3: (1 + i_band) * 3], centre_of_light_position, centre_of_light_velocity ) diff --git a/SOAP/property_calculation/kinematic_properties.py b/SOAP/property_calculation/kinematic_properties.py index cbdabc22..76db7db9 100644 --- a/SOAP/property_calculation/kinematic_properties.py +++ b/SOAP/property_calculation/kinematic_properties.py @@ -63,9 +63,9 @@ def get_rotation_velocity_luminosity_weighted(particle_luminosities: unyt.unyt_a """ number_luminosity_bands = particle_luminosities.shape[1] - rotation_velocities = np.zeros(number_luminosity_bands.shape[0]) * particle_azimuthal_velocities.units + rotation_velocities = np.zeros(number_luminosity_bands) * particle_azimuthal_velocities.units - for i_band, (particle_luminosities_i_band, particle_azimuthal_velocities_i_band) in enumerate(zip(particle_luminosities.T, particle_azimuthal_velocities.T)): + for i_band, (particle_luminosities_i_band, particle_azimuthal_velocities_i_band) in enumerate(zip(particle_luminosities.T, particle_azimuthal_velocities)): luminosity_weights = particle_luminosities_i_band / particle_luminosities_i_band.sum() rotation_velocities[i_band] = get_weighted_rotation_velocity(luminosity_weights, particle_azimuthal_velocities_i_band) @@ -174,9 +174,9 @@ def get_cylindrical_velocity_dispersion_vector_luminosity_weighted( """ number_luminosity_bands = particle_luminosities.shape[1] - velocity_dispersion_vectors = np.zeros((number_luminosity_bands.shape[0], 3)) * particle_cylindrical_velocities.units + velocity_dispersion_vectors = np.zeros((number_luminosity_bands, 3)) * particle_cylindrical_velocities.units - for i_band, (particle_luminosities_i_band, particle_cylindrical_velocities_i_band) in enumerate(zip(particle_luminosities.T, particle_cylindrical_velocities.T)): + for i_band, (particle_luminosities_i_band, particle_cylindrical_velocities_i_band) in enumerate(zip(particle_luminosities.T, particle_cylindrical_velocities)): luminosity_weights = particle_luminosities_i_band / particle_luminosities_i_band.sum() velocity_dispersion_vectors[i_band] = get_weighted_cylindrical_velocity_dispersion_vector(luminosity_weights, particle_cylindrical_velocities_i_band) From 1d9c97da470e2c4c760e53ed962275a062ac70ae Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Tue, 14 Oct 2025 09:57:09 +0100 Subject: [PATCH 18/23] Do not recentre star coordinates for cylindrical velocities We do not do so when computing the angular momentum vector, so this change makes it consistent with our choice to not re-centre. --- SOAP/particle_selection/aperture_properties.py | 7 ------- SOAP/particle_selection/subhalo_properties.py | 7 ------- 2 files changed, 14 deletions(-) diff --git a/SOAP/particle_selection/aperture_properties.py b/SOAP/particle_selection/aperture_properties.py index 9cb9b8bb..f522805d 100644 --- a/SOAP/particle_selection/aperture_properties.py +++ b/SOAP/particle_selection/aperture_properties.py @@ -1472,16 +1472,11 @@ def star_cylindrical_velocities(self) -> unyt.unyt_array: if np.sum(self.Lstar) == 0: return None - # Calculate the position of the stars relative to their CoM. We do not - # use self.com because it is offset and we want to weight differently - com = (self.star_mass_fraction[:, None] * self.pos_star).sum(axis=0) - # Get velocities in cylindrical coordinates return calculate_cylindrical_velocities( self.pos_star, self.vel_star, self.Lstar, - com, self.vcom ) @@ -1546,14 +1541,12 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: for i_band, particle_luminosities_i_band in enumerate(self.stellar_luminosities.T): luminosity_weights = particle_luminosities_i_band / particle_luminosities_i_band.sum() - centre_of_light_position = (luminosity_weights[:, None] * self.pos_star).sum(axis=0) centre_of_light_velocity = (luminosity_weights[:, None] * self.vel_star).sum(axis=0) cylindrical_velocities[i_band] = calculate_cylindrical_velocities( self.pos_star, self.vel_star, self.Lstar_luminosity_weighted[i_band * 3: (1 + i_band) * 3], - centre_of_light_position, centre_of_light_velocity ) diff --git a/SOAP/particle_selection/subhalo_properties.py b/SOAP/particle_selection/subhalo_properties.py index 45817c93..9fab8d41 100644 --- a/SOAP/particle_selection/subhalo_properties.py +++ b/SOAP/particle_selection/subhalo_properties.py @@ -1407,16 +1407,11 @@ def star_cylindrical_velocities(self) -> unyt.unyt_array: if np.sum(self.Lstar) == 0: return None - # Calculate the position of the stars relative to their CoM. We do not - # use self.com because it is offset and we want to weight differently - com = (self.star_mass_fraction[:, None] * self.pos_star).sum(axis=0) - # Get velocities in cylindrical coordinates return calculate_cylindrical_velocities( self.pos_star, self.vel_star, self.Lstar, - com, self.vcom ) @@ -1478,14 +1473,12 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: for i_band, particle_luminosities_i_band in enumerate(self.stellar_luminosities.T): luminosity_weights = particle_luminosities_i_band / particle_luminosities_i_band.sum() - centre_of_light_position = (luminosity_weights[:, None] * self.pos_star).sum(axis=0) centre_of_light_velocity = (luminosity_weights[:, None] * self.vel_star).sum(axis=0) cylindrical_velocities[i_band] = calculate_cylindrical_velocities( self.pos_star, self.vel_star, self.Lstar_luminosity_weighted[i_band * 3: (1 + i_band) * 3], - centre_of_light_position, centre_of_light_velocity ) From daf41373d02716261ee9aab5c80bbbdb4db98d3a Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Tue, 14 Oct 2025 10:00:02 +0100 Subject: [PATCH 19/23] Use stellar centre of mass as reference velocity Including the case for when we do luminosity-weighting. This reflects the same choice we made when computing luminosity-weighted angular momenta. --- SOAP/particle_selection/aperture_properties.py | 8 ++------ SOAP/particle_selection/subhalo_properties.py | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/SOAP/particle_selection/aperture_properties.py b/SOAP/particle_selection/aperture_properties.py index f522805d..1ccfb890 100644 --- a/SOAP/particle_selection/aperture_properties.py +++ b/SOAP/particle_selection/aperture_properties.py @@ -1477,7 +1477,7 @@ def star_cylindrical_velocities(self) -> unyt.unyt_array: self.pos_star, self.vel_star, self.Lstar, - self.vcom + reference_velocity=self.vcom, ) @lazy_property @@ -1539,15 +1539,11 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: # weighted centre of mass phase space coordinates. cylindrical_velocities = np.zeros((self.stellar_luminosities.shape[1], self.stellar_luminosities.shape[0], 3)) * self.vel_star.units for i_band, particle_luminosities_i_band in enumerate(self.stellar_luminosities.T): - - luminosity_weights = particle_luminosities_i_band / particle_luminosities_i_band.sum() - centre_of_light_velocity = (luminosity_weights[:, None] * self.vel_star).sum(axis=0) - cylindrical_velocities[i_band] = calculate_cylindrical_velocities( self.pos_star, self.vel_star, self.Lstar_luminosity_weighted[i_band * 3: (1 + i_band) * 3], - centre_of_light_velocity + reference_velocity=self.vcom, ) return cylindrical_velocities diff --git a/SOAP/particle_selection/subhalo_properties.py b/SOAP/particle_selection/subhalo_properties.py index 9fab8d41..6af4da77 100644 --- a/SOAP/particle_selection/subhalo_properties.py +++ b/SOAP/particle_selection/subhalo_properties.py @@ -1412,7 +1412,7 @@ def star_cylindrical_velocities(self) -> unyt.unyt_array: self.pos_star, self.vel_star, self.Lstar, - self.vcom + reference_velocity=self.vcom, ) @lazy_property @@ -1471,15 +1471,11 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: # weighted centre of mass phase space coordinates. cylindrical_velocities = np.zeros((self.stellar_luminosities.shape[1], self.stellar_luminosities.shape[0], 3)) * self.vel_star.units for i_band, particle_luminosities_i_band in enumerate(self.stellar_luminosities.T): - - luminosity_weights = particle_luminosities_i_band / particle_luminosities_i_band.sum() - centre_of_light_velocity = (luminosity_weights[:, None] * self.vel_star).sum(axis=0) - cylindrical_velocities[i_band] = calculate_cylindrical_velocities( self.pos_star, self.vel_star, self.Lstar_luminosity_weighted[i_band * 3: (1 + i_band) * 3], - centre_of_light_velocity + reference_velocity = self.vcom, ) return cylindrical_velocities From f3be50202d8a0ef83514e41cc471ea6b8a3cd534 Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Tue, 14 Oct 2025 10:07:03 +0100 Subject: [PATCH 20/23] Use STELLAR CoM velocity as reference, not ALL CoM velocity. --- SOAP/particle_selection/aperture_properties.py | 4 ++-- SOAP/particle_selection/subhalo_properties.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SOAP/particle_selection/aperture_properties.py b/SOAP/particle_selection/aperture_properties.py index 1ccfb890..209f021c 100644 --- a/SOAP/particle_selection/aperture_properties.py +++ b/SOAP/particle_selection/aperture_properties.py @@ -1477,7 +1477,7 @@ def star_cylindrical_velocities(self) -> unyt.unyt_array: self.pos_star, self.vel_star, self.Lstar, - reference_velocity=self.vcom, + reference_velocity=self.vcom_star, ) @lazy_property @@ -1543,7 +1543,7 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: self.pos_star, self.vel_star, self.Lstar_luminosity_weighted[i_band * 3: (1 + i_band) * 3], - reference_velocity=self.vcom, + reference_velocity=self.vcom_star, ) return cylindrical_velocities diff --git a/SOAP/particle_selection/subhalo_properties.py b/SOAP/particle_selection/subhalo_properties.py index 6af4da77..5bc6ec13 100644 --- a/SOAP/particle_selection/subhalo_properties.py +++ b/SOAP/particle_selection/subhalo_properties.py @@ -1412,7 +1412,7 @@ def star_cylindrical_velocities(self) -> unyt.unyt_array: self.pos_star, self.vel_star, self.Lstar, - reference_velocity=self.vcom, + reference_velocity=self.vcom_star, ) @lazy_property @@ -1475,7 +1475,7 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: self.pos_star, self.vel_star, self.Lstar_luminosity_weighted[i_band * 3: (1 + i_band) * 3], - reference_velocity = self.vcom, + reference_velocity = self.vcom_star, ) return cylindrical_velocities From 14c2417310a925979d615594bf37498cac0bf5f9 Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Tue, 14 Oct 2025 11:00:41 +0100 Subject: [PATCH 21/23] Fix bug: accidental change of velocities within function --- .../cylindrical_coordinates.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/SOAP/property_calculation/cylindrical_coordinates.py b/SOAP/property_calculation/cylindrical_coordinates.py index 1102b415..a4ded185 100644 --- a/SOAP/property_calculation/cylindrical_coordinates.py +++ b/SOAP/property_calculation/cylindrical_coordinates.py @@ -59,16 +59,22 @@ def calculate_cylindrical_velocities(positions, velocities, z_target, reference_ [v_r, v_phi, v_z] for each particle. """ - if reference_position is not None: - positions -= reference_position - if reference_velocity is not None: - velocities -= reference_velocity + # We need to declare a relative pos/vel array to not overwrite the original + # values. + if reference_position is None: + prel = positions + else: + prel = positions - reference_position + if reference_velocity is None: + vrel = velocities + else: + vrel = velocities - reference_velocity R = build_rotation_matrix(z_target) # Rotate positions and velocities into new frame - positions_rot = positions @ R.T - velocities_rot = velocities @ R.T + positions_rot = prel @ R.T + velocities_rot = vrel @ R.T x = positions_rot[:, 0] y = positions_rot[:, 1] From 0f313df0f43907659c534fb21fd917a87792567f Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Mon, 27 Oct 2025 12:58:47 +0000 Subject: [PATCH 22/23] Run formatter --- .../particle_selection/aperture_properties.py | 75 ++++++++++++------ SOAP/particle_selection/subhalo_properties.py | 76 +++++++++++++------ .../cylindrical_coordinates.py | 4 +- .../kinematic_properties.py | 69 +++++++++++++---- SOAP/property_table.py | 8 +- 5 files changed, 165 insertions(+), 67 deletions(-) diff --git a/SOAP/particle_selection/aperture_properties.py b/SOAP/particle_selection/aperture_properties.py index 209f021c..5d0cbe4b 100644 --- a/SOAP/particle_selection/aperture_properties.py +++ b/SOAP/particle_selection/aperture_properties.py @@ -1487,21 +1487,23 @@ def StellarRotationalVelocity(self) -> unyt.unyt_array: """ if (self.Nstar < 2) or (np.sum(self.Lstar) == 0): return None - return get_rotation_velocity_mass_weighted(self.mass_star, self.star_cylindrical_velocities[:,1]) + return get_rotation_velocity_mass_weighted( + self.mass_star, self.star_cylindrical_velocities[:, 1] + ) @lazy_property def StellarCylindricalVelocityDispersionVector(self) -> unyt.unyt_array: if (self.Nstar < 2) or (np.sum(self.Lstar) == 0): return None - return get_cylindrical_velocity_dispersion_vector_mass_weighted(self.mass_star, self.star_cylindrical_velocities) + return get_cylindrical_velocity_dispersion_vector_mass_weighted( + self.mass_star, self.star_cylindrical_velocities + ) @lazy_property def StellarCylindricalVelocityDispersion(self) -> unyt.unyt_array: if self.StellarCylindricalVelocityDispersionVector is None: return None - return np.sqrt( - (self.StellarCylindricalVelocityDispersionVector**2).sum() / 3 - ) + return np.sqrt((self.StellarCylindricalVelocityDispersionVector**2).sum() / 3) @lazy_property def StellarCylindricalVelocityDispersionVertical(self) -> unyt.unyt_array: @@ -1513,9 +1515,7 @@ def StellarCylindricalVelocityDispersionVertical(self) -> unyt.unyt_array: def StellarCylindricalVelocityDispersionDiscPlane(self) -> unyt.unyt_array: if self.StellarCylindricalVelocityDispersionVector is None: return None - return np.sqrt( - (self.StellarCylindricalVelocityDispersionVector[:2]**2).sum() - ) + return np.sqrt((self.StellarCylindricalVelocityDispersionVector[:2] ** 2).sum()) @lazy_property def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: @@ -1537,14 +1537,25 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: # We iterate over bands to use their own reference vector and luminosity- # weighted centre of mass phase space coordinates. - cylindrical_velocities = np.zeros((self.stellar_luminosities.shape[1], self.stellar_luminosities.shape[0], 3)) * self.vel_star.units - for i_band, particle_luminosities_i_band in enumerate(self.stellar_luminosities.T): + cylindrical_velocities = ( + np.zeros( + ( + self.stellar_luminosities.shape[1], + self.stellar_luminosities.shape[0], + 3, + ) + ) + * self.vel_star.units + ) + for i_band, particle_luminosities_i_band in enumerate( + self.stellar_luminosities.T + ): cylindrical_velocities[i_band] = calculate_cylindrical_velocities( - self.pos_star, - self.vel_star, - self.Lstar_luminosity_weighted[i_band * 3: (1 + i_band) * 3], - reference_velocity=self.vcom_star, - ) + self.pos_star, + self.vel_star, + self.Lstar_luminosity_weighted[i_band * 3 : (1 + i_band) * 3], + reference_velocity=self.vcom_star, + ) return cylindrical_velocities @@ -1552,34 +1563,52 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: def StellarRotationalVelocityLuminosityWeighted(self) -> unyt.unyt_array: if (self.Nstar < 2) or (np.sum(self.Lstar) == 0): return None - return get_rotation_velocity_luminosity_weighted(self.stellar_luminosities, self.star_cylindrical_velocities_luminosity_weighted[:,:,1]) + return get_rotation_velocity_luminosity_weighted( + self.stellar_luminosities, + self.star_cylindrical_velocities_luminosity_weighted[:, :, 1], + ) @lazy_property - def StellarCylindricalVelocityDispersionVectorLuminosityWeighted(self) -> unyt.unyt_array: + def StellarCylindricalVelocityDispersionVectorLuminosityWeighted( + self, + ) -> unyt.unyt_array: if (self.Nstar < 2) or (np.sum(self.Lstar) == 0): return None - return get_cylindrical_velocity_dispersion_vector_luminosity_weighted(self.stellar_luminosities, self.star_cylindrical_velocities_luminosity_weighted) + return get_cylindrical_velocity_dispersion_vector_luminosity_weighted( + self.stellar_luminosities, + self.star_cylindrical_velocities_luminosity_weighted, + ) @lazy_property def StellarCylindricalVelocityDispersionLuminosityWeighted(self) -> unyt.unyt_array: if self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted is None: return None return np.sqrt( - (self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted**2).sum(axis=1) / 3 + ( + self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted**2 + ).sum(axis=1) + / 3 ) @lazy_property - def StellarCylindricalVelocityDispersionVerticalLuminosityWeighted(self) -> unyt.unyt_array: + def StellarCylindricalVelocityDispersionVerticalLuminosityWeighted( + self, + ) -> unyt.unyt_array: if self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted is None: return None - return self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted[:,2] + return self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted[:, 2] @lazy_property - def StellarCylindricalVelocityDispersionDiscPlaneLuminosityWeighted(self) -> unyt.unyt_array: + def StellarCylindricalVelocityDispersionDiscPlaneLuminosityWeighted( + self, + ) -> unyt.unyt_array: if self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted is None: return None return np.sqrt( - (self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted[:,:2]**2).sum(axis=1) + ( + self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted[:, :2] + ** 2 + ).sum(axis=1) ) @lazy_property diff --git a/SOAP/particle_selection/subhalo_properties.py b/SOAP/particle_selection/subhalo_properties.py index 5bc6ec13..bd2c94cd 100644 --- a/SOAP/particle_selection/subhalo_properties.py +++ b/SOAP/particle_selection/subhalo_properties.py @@ -1419,21 +1419,23 @@ def star_cylindrical_velocities(self) -> unyt.unyt_array: def StellarRotationalVelocity(self) -> unyt.unyt_array: if (self.Nstar < 2) or (np.sum(self.Lstar) == 0): return None - return get_rotation_velocity_mass_weighted(self.mass_star, self.star_cylindrical_velocities[:,1]) + return get_rotation_velocity_mass_weighted( + self.mass_star, self.star_cylindrical_velocities[:, 1] + ) @lazy_property def StellarCylindricalVelocityDispersionVector(self) -> unyt.unyt_array: if (self.Nstar < 2) or (np.sum(self.Lstar) == 0): return None - return get_cylindrical_velocity_dispersion_vector_mass_weighted(self.mass_star, self.star_cylindrical_velocities) + return get_cylindrical_velocity_dispersion_vector_mass_weighted( + self.mass_star, self.star_cylindrical_velocities + ) @lazy_property def StellarCylindricalVelocityDispersion(self) -> unyt.unyt_array: if self.StellarCylindricalVelocityDispersionVector is None: return None - return np.sqrt( - (self.StellarCylindricalVelocityDispersionVector**2).sum() / 3 - ) + return np.sqrt((self.StellarCylindricalVelocityDispersionVector**2).sum() / 3) @lazy_property def StellarCylindricalVelocityDispersionVertical(self) -> unyt.unyt_array: @@ -1445,9 +1447,7 @@ def StellarCylindricalVelocityDispersionVertical(self) -> unyt.unyt_array: def StellarCylindricalVelocityDispersionDiscPlane(self) -> unyt.unyt_array: if self.StellarCylindricalVelocityDispersionVector is None: return None - return np.sqrt( - (self.StellarCylindricalVelocityDispersionVector[:2]**2).sum() - ) + return np.sqrt((self.StellarCylindricalVelocityDispersionVector[:2] ** 2).sum()) @lazy_property def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: @@ -1469,14 +1469,25 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: # We iterate over bands to use their own reference vector and luminosity- # weighted centre of mass phase space coordinates. - cylindrical_velocities = np.zeros((self.stellar_luminosities.shape[1], self.stellar_luminosities.shape[0], 3)) * self.vel_star.units - for i_band, particle_luminosities_i_band in enumerate(self.stellar_luminosities.T): + cylindrical_velocities = ( + np.zeros( + ( + self.stellar_luminosities.shape[1], + self.stellar_luminosities.shape[0], + 3, + ) + ) + * self.vel_star.units + ) + for i_band, particle_luminosities_i_band in enumerate( + self.stellar_luminosities.T + ): cylindrical_velocities[i_band] = calculate_cylindrical_velocities( - self.pos_star, - self.vel_star, - self.Lstar_luminosity_weighted[i_band * 3: (1 + i_band) * 3], - reference_velocity = self.vcom_star, - ) + self.pos_star, + self.vel_star, + self.Lstar_luminosity_weighted[i_band * 3 : (1 + i_band) * 3], + reference_velocity=self.vcom_star, + ) return cylindrical_velocities @@ -1484,34 +1495,53 @@ def star_cylindrical_velocities_luminosity_weighted(self) -> unyt.unyt_array: def StellarRotationalVelocityLuminosityWeighted(self) -> unyt.unyt_array: if (self.Nstar < 2) or (np.sum(self.Lstar) == 0): return None - return get_rotation_velocity_luminosity_weighted(self.stellar_luminosities, self.star_cylindrical_velocities_luminosity_weighted[:,:,1]) + + return get_rotation_velocity_luminosity_weighted( + self.stellar_luminosities, + self.star_cylindrical_velocities_luminosity_weighted[:, :, 1], + ) @lazy_property - def StellarCylindricalVelocityDispersionVectorLuminosityWeighted(self) -> unyt.unyt_array: + def StellarCylindricalVelocityDispersionVectorLuminosityWeighted( + self, + ) -> unyt.unyt_array: if (self.Nstar < 2) or (np.sum(self.Lstar) == 0): return None - return get_cylindrical_velocity_dispersion_vector_luminosity_weighted(self.stellar_luminosities, self.star_cylindrical_velocities_luminosity_weighted) + return get_cylindrical_velocity_dispersion_vector_luminosity_weighted( + self.stellar_luminosities, + self.star_cylindrical_velocities_luminosity_weighted, + ) @lazy_property def StellarCylindricalVelocityDispersionLuminosityWeighted(self) -> unyt.unyt_array: if self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted is None: return None return np.sqrt( - (self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted**2).sum(axis=1) / 3 + ( + self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted**2 + ).sum(axis=1) + / 3 ) @lazy_property - def StellarCylindricalVelocityDispersionVerticalLuminosityWeighted(self) -> unyt.unyt_array: + def StellarCylindricalVelocityDispersionVerticalLuminosityWeighted( + self, + ) -> unyt.unyt_array: if self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted is None: return None - return self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted[:,2] + return self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted[:, 2] @lazy_property - def StellarCylindricalVelocityDispersionDiscPlaneLuminosityWeighted(self) -> unyt.unyt_array: + def StellarCylindricalVelocityDispersionDiscPlaneLuminosityWeighted( + self, + ) -> unyt.unyt_array: if self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted is None: return None return np.sqrt( - (self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted[:,:2]**2).sum(axis=1) + ( + self.StellarCylindricalVelocityDispersionVectorLuminosityWeighted[:, :2] + ** 2 + ).sum(axis=1) ) @lazy_property diff --git a/SOAP/property_calculation/cylindrical_coordinates.py b/SOAP/property_calculation/cylindrical_coordinates.py index a4ded185..410b01c4 100644 --- a/SOAP/property_calculation/cylindrical_coordinates.py +++ b/SOAP/property_calculation/cylindrical_coordinates.py @@ -42,7 +42,9 @@ def build_rotation_matrix(z_target): return R -def calculate_cylindrical_velocities(positions, velocities, z_target, reference_position = None, reference_velocity = None): +def calculate_cylindrical_velocities( + positions, velocities, z_target, reference_position=None, reference_velocity=None +): """ Convert 3D Cartesian velocities to cylindrical coordinates (v_r, v_phi, v_z), after rotating the system such that the z-axis aligns with `z_target`. diff --git a/SOAP/property_calculation/kinematic_properties.py b/SOAP/property_calculation/kinematic_properties.py index 76db7db9..1e5c900f 100644 --- a/SOAP/property_calculation/kinematic_properties.py +++ b/SOAP/property_calculation/kinematic_properties.py @@ -14,8 +14,9 @@ import unyt -def get_weighted_rotation_velocity(particle_weights: unyt.unyt_array, - particle_azimuthal_velocities: unyt.unyt_array) -> unyt.unyt_quantity: +def get_weighted_rotation_velocity( + particle_weights: unyt.unyt_array, particle_azimuthal_velocities: unyt.unyt_array +) -> unyt.unyt_quantity: """ Get the weighted average azimuthal velocity of a particle distribution. @@ -30,7 +31,10 @@ def get_weighted_rotation_velocity(particle_weights: unyt.unyt_array, """ return (particle_weights * particle_azimuthal_velocities).sum() -def get_rotation_velocity_mass_weighted(particle_masses, particle_azimuthal_velocities) -> unyt.unyt_quantity: + +def get_rotation_velocity_mass_weighted( + particle_masses, particle_azimuthal_velocities +) -> unyt.unyt_quantity: """ Return the mass-weighted average azimuthal velocity of a particle distribution. @@ -46,8 +50,11 @@ def get_rotation_velocity_mass_weighted(particle_masses, particle_azimuthal_velo mass_weights = particle_masses / particle_masses.sum() return get_weighted_rotation_velocity(mass_weights, particle_azimuthal_velocities) -def get_rotation_velocity_luminosity_weighted(particle_luminosities: unyt.unyt_array, - particle_azimuthal_velocities: unyt.unyt_array) -> unyt.unyt_array: + +def get_rotation_velocity_luminosity_weighted( + particle_luminosities: unyt.unyt_array, + particle_azimuthal_velocities: unyt.unyt_array, +) -> unyt.unyt_array: """ Return the luminosity-weighted average azimuthal velocity of a particle distribution, for each provided luminosity band. @@ -63,14 +70,24 @@ def get_rotation_velocity_luminosity_weighted(particle_luminosities: unyt.unyt_a """ number_luminosity_bands = particle_luminosities.shape[1] - rotation_velocities = np.zeros(number_luminosity_bands) * particle_azimuthal_velocities.units + rotation_velocities = ( + np.zeros(number_luminosity_bands) * particle_azimuthal_velocities.units + ) - for i_band, (particle_luminosities_i_band, particle_azimuthal_velocities_i_band) in enumerate(zip(particle_luminosities.T, particle_azimuthal_velocities)): - luminosity_weights = particle_luminosities_i_band / particle_luminosities_i_band.sum() - rotation_velocities[i_band] = get_weighted_rotation_velocity(luminosity_weights, particle_azimuthal_velocities_i_band) + for i_band, ( + particle_luminosities_i_band, + particle_azimuthal_velocities_i_band, + ) in enumerate(zip(particle_luminosities.T, particle_azimuthal_velocities)): + luminosity_weights = ( + particle_luminosities_i_band / particle_luminosities_i_band.sum() + ) + rotation_velocities[i_band] = get_weighted_rotation_velocity( + luminosity_weights, particle_azimuthal_velocities_i_band + ) return rotation_velocities + def get_velocity_dispersion_matrix( mass_fraction: unyt.unyt_array, velocity: unyt.unyt_array, @@ -109,6 +126,7 @@ def get_velocity_dispersion_matrix( return result + def get_weighted_cylindrical_velocity_dispersion_vector( particle_weights: unyt.unyt_array, particle_cylindrical_velocities: unyt.unyt_array, @@ -127,13 +145,17 @@ def get_weighted_cylindrical_velocity_dispersion_vector( """ # This implementation of standard deviation is more numerically stable than using - ^2 - mean_velocity = (particle_weights[:, None] * particle_cylindrical_velocities).sum(axis=0) + mean_velocity = (particle_weights[:, None] * particle_cylindrical_velocities).sum( + axis=0 + ) squared_velocity_dispersion = ( - particle_weights[:, None] * (particle_cylindrical_velocities - mean_velocity) ** 2 + particle_weights[:, None] + * (particle_cylindrical_velocities - mean_velocity) ** 2 ).sum(axis=0) return np.sqrt(squared_velocity_dispersion) + def get_cylindrical_velocity_dispersion_vector_mass_weighted( particle_masses: unyt.unyt_array, particle_cylindrical_velocities: unyt.unyt_array, @@ -151,7 +173,10 @@ def get_cylindrical_velocity_dispersion_vector_mass_weighted( Returns a 3 element vector containing [sigma_r, sigma_phi, sigma_z]. """ mass_weights = particle_masses / particle_masses.sum() - return get_weighted_cylindrical_velocity_dispersion_vector(mass_weights, particle_cylindrical_velocities) + return get_weighted_cylindrical_velocity_dispersion_vector( + mass_weights, particle_cylindrical_velocities + ) + def get_cylindrical_velocity_dispersion_vector_luminosity_weighted( particle_luminosities: unyt.unyt_array, @@ -174,14 +199,26 @@ def get_cylindrical_velocity_dispersion_vector_luminosity_weighted( """ number_luminosity_bands = particle_luminosities.shape[1] - velocity_dispersion_vectors = np.zeros((number_luminosity_bands, 3)) * particle_cylindrical_velocities.units + velocity_dispersion_vectors = ( + np.zeros((number_luminosity_bands, 3)) * particle_cylindrical_velocities.units + ) - for i_band, (particle_luminosities_i_band, particle_cylindrical_velocities_i_band) in enumerate(zip(particle_luminosities.T, particle_cylindrical_velocities)): - luminosity_weights = particle_luminosities_i_band / particle_luminosities_i_band.sum() - velocity_dispersion_vectors[i_band] = get_weighted_cylindrical_velocity_dispersion_vector(luminosity_weights, particle_cylindrical_velocities_i_band) + for i_band, ( + particle_luminosities_i_band, + particle_cylindrical_velocities_i_band, + ) in enumerate(zip(particle_luminosities.T, particle_cylindrical_velocities)): + luminosity_weights = ( + particle_luminosities_i_band / particle_luminosities_i_band.sum() + ) + velocity_dispersion_vectors[i_band] = ( + get_weighted_cylindrical_velocity_dispersion_vector( + luminosity_weights, particle_cylindrical_velocities_i_band + ) + ) return velocity_dispersion_vectors + def get_angular_momentum( mass: unyt.unyt_array, position: unyt.unyt_array, diff --git a/SOAP/property_table.py b/SOAP/property_table.py index ecb00c8a..bdca0efe 100644 --- a/SOAP/property_table.py +++ b/SOAP/property_table.py @@ -1718,7 +1718,7 @@ class PropertyTable: ), "StellarRotationalVelocityLuminosityWeighted": Property( name="StellarRotationalVelocityLuminosityWeighted", - shape=9, # GAMA bands + shape=9, # GAMA bands dtype=np.float32, unit="snap_length/snap_time", description="Luminosity-weighted mean rotational velocity of the stars, in a cylindrical coordinate system where the axes are centred on the stellar CoM, and the z axis is aligned with the stellar angular momentum.", @@ -1734,7 +1734,7 @@ class PropertyTable: ), "StellarCylindricalVelocityDispersionLuminosityWeighted": Property( name="StellarCylindricalVelocityDispersionLuminosityWeighted", - shape=9, # GAMA bands + shape=9, # GAMA bands dtype=np.float32, unit="snap_length/snap_time", description="One-dimensional luminosity-weighted velocity dispersion of the stars computed in a cylindrical coordinate system where the axes are centred on the stellar CoM, and the z axis is aligned with the stellar angular momentum.", @@ -1750,7 +1750,7 @@ class PropertyTable: ), "StellarCylindricalVelocityDispersionVerticalLuminosityWeighted": Property( name="StellarCylindricalVelocityDispersionVerticalLuminosityWeighted", - shape=9, # GAMA bands + shape=9, # GAMA bands dtype=np.float32, unit="snap_length/snap_time", description="Velocity dispersion perpendicular to the orbital plane of the stars, luminosity-weighted and computed in a cylindrical coordinate system where the axes are centred on the stellar CoM, and the z axis is aligned with the stellar angular momentum.", @@ -1766,7 +1766,7 @@ class PropertyTable: ), "StellarCylindricalVelocityDispersionDiscPlaneLuminosityWeighted": Property( name="StellarCylindricalVelocityDispersionDiscPlaneLuminosityWeighted", - shape=9, # GAMA bands + shape=9, # GAMA bands dtype=np.float32, unit="snap_length/snap_time", description="Total velocity dispersion in the orbital plane of the stars, luminosity-weighted and computed in a cylindrical coordinate system where the axes are centred on the stellar CoM, and the z axis is aligned with the stellar angular momentum.", From c5c64445443e655a015ea91de141be0d28160edf Mon Sep 17 00:00:00 2001 From: Victor Forouhar Date: Mon, 27 Oct 2025 14:46:11 +0000 Subject: [PATCH 23/23] Update property table. --- SOAP/property_table.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/SOAP/property_table.py b/SOAP/property_table.py index bdca0efe..f10b4f99 100644 --- a/SOAP/property_table.py +++ b/SOAP/property_table.py @@ -1657,7 +1657,7 @@ class PropertyTable: shape=1, dtype=np.float32, unit="snap_length/snap_time", - description="Mass weighted mean rotational velocity of the stars, in a cylindrical coordinate system where the axes are centred on the stellar CoM, and the z axis is aligned with the stellar angular momentum.", + description="Mass-weighted mean rotational velocity of the stars, in a cylindrical coordinate system where the axes are centred on the halo centre, the z axis is aligned with the mass-weighted stellar angular momentum, and stellar velocities are relative to the stellar centre of mass velocity.", lossy_compression_filter="FMantissa9", dmo_property=False, particle_properties=[ @@ -1673,7 +1673,7 @@ class PropertyTable: shape=1, dtype=np.float32, unit="snap_length/snap_time", - description="One-dimensional velocity dispersion of the stars computed in a cylindrical coordinate system where the axes are centred on the stellar CoM, and the z axis is aligned with the stellar angular momentum.", + description="One-dimensional mass-weighted velocity dispersion of the star, in a cylindrical coordinate system where the axes are centred on the halo centre, the z axis is aligned with the mass-weighted stellar angular momentum, and stellar velocities are relative to the stellar centre of mass velocity.", lossy_compression_filter="FMantissa9", dmo_property=False, particle_properties=[ @@ -1689,7 +1689,7 @@ class PropertyTable: shape=1, dtype=np.float32, unit="snap_length/snap_time", - description="Velocity dispersion perpendicular to the orbital plane of the stars, computed in a cylindrical coordinate system where the axes are centred on the stellar CoM, and the z axis is aligned with the stellar angular momentum.", + description="Mass-weighted velocity dispersion perpendicular to the orbital plane of the stars, in a cylindrical coordinate system where the axes are centred on the halo centre, the z axis is aligned with the mass-weighted stellar angular momentum, and stellar velocities are relative to the stellar centre of mass velocity.", lossy_compression_filter="FMantissa9", dmo_property=False, particle_properties=[ @@ -1705,7 +1705,7 @@ class PropertyTable: shape=1, dtype=np.float32, unit="snap_length/snap_time", - description="Total velocity dispersion in the orbital plane of the stars, computed in a cylindrical coordinate system where the axes are centred on the stellar CoM, and the z axis is aligned with the stellar angular momentum.", + description="Mass-weighted total velocity dispersion in the orbital plane of the stars, in a cylindrical coordinate system where the axes are centred on the halo centre, the z axis is aligned with the mass-weighted stellar angular momentum, and stellar velocities are relative to the stellar centre of mass velocity.", lossy_compression_filter="FMantissa9", dmo_property=False, particle_properties=[ @@ -1721,7 +1721,7 @@ class PropertyTable: shape=9, # GAMA bands dtype=np.float32, unit="snap_length/snap_time", - description="Luminosity-weighted mean rotational velocity of the stars, in a cylindrical coordinate system where the axes are centred on the stellar CoM, and the z axis is aligned with the stellar angular momentum.", + description="Luminosity-weighted mean rotational velocity of the stars, in a cylindrical coordinate system where the axes are centred on the halo centre, the z axis is aligned with the luminosity-weighted stellar angular momentum, and stellar velocities are relative to the stellar centre of mass velocity.", lossy_compression_filter="FMantissa9", dmo_property=False, particle_properties=[ @@ -1737,7 +1737,7 @@ class PropertyTable: shape=9, # GAMA bands dtype=np.float32, unit="snap_length/snap_time", - description="One-dimensional luminosity-weighted velocity dispersion of the stars computed in a cylindrical coordinate system where the axes are centred on the stellar CoM, and the z axis is aligned with the stellar angular momentum.", + description="One-dimensional luminosity-weighted velocity dispersion of the stars, in a cylindrical coordinate system where the axes are centred on the halo centre, the z axis is aligned with the luminosity-weighted stellar angular momentum, and stellar velocities are relative to the stellar centre of mass velocity.", lossy_compression_filter="FMantissa9", dmo_property=False, particle_properties=[ @@ -1753,7 +1753,7 @@ class PropertyTable: shape=9, # GAMA bands dtype=np.float32, unit="snap_length/snap_time", - description="Velocity dispersion perpendicular to the orbital plane of the stars, luminosity-weighted and computed in a cylindrical coordinate system where the axes are centred on the stellar CoM, and the z axis is aligned with the stellar angular momentum.", + description="Luminosity-weighted velocity dispersion perpendicular to the orbital plane of the stars, in a cylindrical coordinate system where the axes are centred on the halo centre, the z axis is aligned with the luminosity-weighted stellar angular momentum, and stellar velocities are relative to the stellar centre of mass velocity.", lossy_compression_filter="FMantissa9", dmo_property=False, particle_properties=[ @@ -1769,7 +1769,7 @@ class PropertyTable: shape=9, # GAMA bands dtype=np.float32, unit="snap_length/snap_time", - description="Total velocity dispersion in the orbital plane of the stars, luminosity-weighted and computed in a cylindrical coordinate system where the axes are centred on the stellar CoM, and the z axis is aligned with the stellar angular momentum.", + description="Luminosity-weighted total velocity dispersion in the orbital plane of the stars, in a cylindrical coordinate system where the axes are centred on the halo centre, the z axis is aligned with the luminosity-weighted stellar angular momentum, and stellar velocities are relative to the stellar centre of mass velocity.", lossy_compression_filter="FMantissa9", dmo_property=False, particle_properties=[