Skip to content

Commit 1e7b2c5

Browse files
Merge pull request #5001 from jzuhone/particle_vlos
ENH: Support line-of-sight fields for particle types
2 parents 7ebe857 + 27d3b69 commit 1e7b2c5

File tree

5 files changed

+64
-8
lines changed

5 files changed

+64
-8
lines changed

doc/source/analyzing/fields.rst

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -702,16 +702,20 @@ you want to examine a line-of-sight vector within a 3-D data object, set the
702702
# Set to a three-vector for an off-axis component
703703
dd.set_field_parameter("axis", [0.3, 0.4, -0.7])
704704
print(dd["gas", "velocity_los"])
705+
# particle fields are supported too!
706+
print(dd["all", "particle_velocity_los"])
705707
706708
.. warning::
707709

708710
If you need to change the axis of the line of sight on the *same* data container
709-
(sphere, box, cylinder, or whatever), you will need to delete the field using
711+
(sphere, box, cylinder, or whatever), you will need to delete the field using (e.g.)
710712
``del dd['velocity_los']`` and re-generate it.
711713

712714
At this time, this functionality is enabled for the velocity and magnetic vector
713-
fields, ``('gas', 'velocity_los')`` and ``('gas', 'magnetic_field_los')``. The
714-
following fields built into yt make use of these line-of-sight fields:
715+
fields, ``('gas', 'velocity_los')`` and ``('gas', 'magnetic_field_los')`` for
716+
the ``"gas"`` field type, as well as every particle type with
717+
a velocity field, e.g. ``("all", "particle_velocity_los")``. The following fields
718+
built into yt make use of these line-of-sight fields:
715719

716720
* ``('gas', 'sz_kinetic')`` uses ``('gas', 'velocity_los')``
717721
* ``('gas', 'rotation_measure')`` uses ``('gas', 'magnetic_field_los')``

yt/data_objects/tests/test_data_containers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ def test_derived_field(self):
160160
# their parent field to be created
161161
ds = fake_particle_ds()
162162
dd = ds.all_data()
163+
dd.set_field_parameter("axis", 0)
163164

164165
@particle_filter(requires=["particle_mass"], filtered_type="io")
165166
def massive(pfilter, data):

yt/fields/particle_fields.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
)
2323

2424
from .field_functions import get_radius
25-
from .vector_operations import create_magnitude_field
25+
from .vector_operations import create_los_field, create_magnitude_field
2626

2727
sph_whitelist_fields = (
2828
"density",
@@ -329,6 +329,14 @@ def _particle_velocity_magnitude(field, data):
329329
units=unit_system["velocity"],
330330
)
331331

332+
create_los_field(
333+
registry,
334+
"particle_velocity",
335+
unit_system["velocity"],
336+
ftype=ptype,
337+
sampling_type="particle",
338+
)
339+
332340
def _particle_specific_angular_momentum(field, data):
333341
"""Calculate the angular of a particle velocity.
334342

yt/fields/tests/test_particle_fields.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import numpy as np
2+
13
from yt.testing import assert_allclose_units, requires_file, requires_module
24
from yt.utilities.answer_testing.framework import data_dir_load
35

@@ -19,3 +21,28 @@ def test_relative_particle_fields():
1921
assert_allclose_units(
2022
sp["all", "relative_particle_velocity"], sp["all", "particle_velocity"] - bv
2123
)
24+
25+
26+
@requires_module("h5py")
27+
@requires_file(g30)
28+
def test_los_particle_fields():
29+
ds = data_dir_load(g30)
30+
offset = ds.arr([0.1, -0.2, 0.3], "code_length")
31+
c = ds.domain_center + offset
32+
sp = ds.sphere(c, (10, "kpc"))
33+
bv = ds.arr([1.0, 2.0, 3.0], "code_velocity")
34+
sp.set_field_parameter("bulk_velocity", bv)
35+
ax = [0.1, 0.2, -0.3]
36+
sp.set_field_parameter("axis", ax)
37+
ax /= np.linalg.norm(ax)
38+
vlos = (
39+
sp["all", "relative_particle_velocity_x"] * ax[0]
40+
+ sp["all", "relative_particle_velocity_y"] * ax[1]
41+
+ sp["all", "relative_particle_velocity_z"] * ax[2]
42+
)
43+
assert_allclose_units(sp["all", "particle_velocity_los"], vlos)
44+
sp.clear_data()
45+
ax = 2
46+
sp.set_field_parameter("axis", ax)
47+
vlos = sp["all", "relative_particle_velocity_z"]
48+
assert_allclose_units(sp["all", "particle_velocity_los"], vlos)

yt/fields/vector_operations.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,18 +104,34 @@ def _relative_vector(field, data):
104104
)
105105

106106

107-
def create_los_field(registry, basename, field_units, ftype="gas", slice_info=None):
107+
def create_los_field(
108+
registry,
109+
basename,
110+
field_units,
111+
ftype="gas",
112+
slice_info=None,
113+
*,
114+
sampling_type="local",
115+
):
108116
axis_order = registry.ds.coordinates.axis_order
109117

118+
# Here we need to check if we are a particle field, so that we can
119+
# correctly identify the "bulk" field parameter corresponding to
120+
# this vector field.
121+
if sampling_type == "particle":
122+
basenm = basename.removeprefix("particle_")
123+
else:
124+
basenm = basename
125+
110126
validators = [
111-
ValidateParameter(f"bulk_{basename}"),
127+
ValidateParameter(f"bulk_{basenm}"),
112128
ValidateParameter("axis", {"axis": [0, 1, 2]}),
113129
]
114130

115131
field_comps = [(ftype, f"{basename}_{ax}") for ax in axis_order]
116132

117133
def _los_field(field, data):
118-
if data.has_field_parameter(f"bulk_{basename}"):
134+
if data.has_field_parameter(f"bulk_{basenm}"):
119135
fns = [(fc[0], f"relative_{fc[1]}") for fc in field_comps]
120136
else:
121137
fns = field_comps
@@ -132,7 +148,7 @@ def _los_field(field, data):
132148

133149
registry.add_field(
134150
(ftype, f"{basename}_los"),
135-
sampling_type="local",
151+
sampling_type=sampling_type,
136152
function=_los_field,
137153
units=field_units,
138154
validators=validators,

0 commit comments

Comments
 (0)