Skip to content

Commit 5debb8a

Browse files
committed
apply log filtering to each instrument
1 parent 2b0c50b commit 5debb8a

8 files changed

Lines changed: 157 additions & 106 deletions

File tree

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
"""Measurement instrument that can be used with Parcels."""
22

3-
from . import adcp, argo_float, ctd, drifter, ship_underwater_st, xbt
3+
from . import adcp, argo_float, ctd, ctd_bgc, drifter, ship_underwater_st, xbt
44

5-
__all__ = ["adcp", "argo_float", "ctd", "drifter", "ship_underwater_st", "xbt"]
5+
__all__ = [
6+
"adcp",
7+
"argo_float",
8+
"ctd",
9+
"ctd_bgc",
10+
"drifter",
11+
"ship_underwater_st",
12+
"xbt",
13+
]

src/virtualship/instruments/adcp.py

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
"""ADCP instrument."""
22

3-
import logging
43
from pathlib import Path
54

65
import numpy as np
76
from parcels import FieldSet, ParticleSet, ScipyParticle, Variable
87

9-
from ..log_filter import DuplicateFilter
8+
from ..log_filter import Filter, external_logger
109
from ..spacetime import Spacetime
1110

1211
# we specifically use ScipyParticle because we have many small calls to execute
@@ -32,7 +31,6 @@ def simulate_adcp(
3231
min_depth: float,
3332
num_bins: int,
3433
sample_points: list[Spacetime],
35-
log_filter: bool = True,
3634
) -> None:
3735
"""
3836
Use Parcels to simulate an ADCP in a fieldset.
@@ -43,7 +41,6 @@ def simulate_adcp(
4341
:param min_depth: Minimum depth the ADCP can measure.
4442
:param num_bins: How many samples to take in the complete range between max_depth and min_depth.
4543
:param sample_points: The places and times to sample at.
46-
:param log_filter: Whether to filter duplicate log messages (defaults to True). This is a bit of a hack, but it works and could be removed if changed in Parcels.
4744
"""
4845
sample_points.sort(key=lambda p: p.time)
4946

@@ -64,31 +61,29 @@ def simulate_adcp(
6461
# outputdt set to infinite as we just want to write at the end of every call to 'execute'
6562
out_file = particleset.ParticleFile(name=out_path, outputdt=np.inf)
6663

67-
# whether to filter parcels duplicate log messages
68-
if log_filter:
69-
external_logger = logging.getLogger("parcels.tools.loggers")
70-
for handler in external_logger.handlers:
71-
handler.addFilter(DuplicateFilter())
64+
# filter out Parcels logging messages
65+
for handler in external_logger.handlers:
66+
handler.addFilter(Filter())
7267

73-
for point in sample_points:
74-
particleset.lon_nextloop[:] = point.location.lon
75-
particleset.lat_nextloop[:] = point.location.lat
76-
particleset.time_nextloop[:] = fieldset.time_origin.reltime(
77-
np.datetime64(point.time)
78-
)
68+
# try/finally to ensure filter is always removed even if .execute fails (to avoid filter being appled universally)
69+
try:
70+
for point in sample_points:
71+
particleset.lon_nextloop[:] = point.location.lon
72+
particleset.lat_nextloop[:] = point.location.lat
73+
particleset.time_nextloop[:] = fieldset.time_origin.reltime(
74+
np.datetime64(point.time)
75+
)
7976

80-
# perform one step using the particleset
81-
# dt and runtime are set so exactly one step is made.
82-
particleset.execute(
83-
[_sample_velocity],
84-
dt=1,
85-
runtime=1,
86-
verbose_progress=False,
87-
output_file=out_file,
88-
)
77+
# perform one step using the particleset
78+
# dt and runtime are set so exactly one step is made.
79+
particleset.execute(
80+
[_sample_velocity],
81+
dt=1,
82+
runtime=1,
83+
verbose_progress=False,
84+
output_file=out_file,
85+
)
8986

90-
# turn off log filter after .execute(), to prevent being applied universally to all loggers
91-
# separate if statement from above to prevent error if log_filter is False
92-
if log_filter:
87+
finally:
9388
for handler in external_logger.handlers:
9489
handler.removeFilter(handler.filters[0])

src/virtualship/instruments/argo_float.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
Variable,
1616
)
1717

18+
from ..log_filter import Filter, external_logger
1819
from ..spacetime import Spacetime
1920

2021

@@ -171,16 +172,26 @@ def simulate_argo_floats(
171172
else:
172173
actual_endtime = np.timedelta64(endtime)
173174

174-
# execute simulation
175-
argo_float_particleset.execute(
176-
[
177-
_argo_float_vertical_movement,
178-
AdvectionRK4,
179-
_keep_at_surface,
180-
_check_error,
181-
],
182-
endtime=actual_endtime,
183-
dt=DT,
184-
output_file=out_file,
185-
verbose_progress=True,
186-
)
175+
# filter out Parcels logging messages
176+
for handler in external_logger.handlers:
177+
handler.addFilter(Filter())
178+
179+
# try/finally to ensure filter is always removed even if .execute fails (to avoid filter being appled universally)
180+
try:
181+
# execute simulation
182+
argo_float_particleset.execute(
183+
[
184+
_argo_float_vertical_movement,
185+
AdvectionRK4,
186+
_keep_at_surface,
187+
_check_error,
188+
],
189+
endtime=actual_endtime,
190+
dt=DT,
191+
output_file=out_file,
192+
verbose_progress=False,
193+
)
194+
195+
finally:
196+
for handler in external_logger.handlers:
197+
handler.removeFilter(handler.filters[0])

src/virtualship/instruments/ctd.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import numpy as np
88
from parcels import FieldSet, JITParticle, ParticleSet, Variable
99

10+
from ..log_filter import Filter, external_logger
1011
from ..spacetime import Spacetime
1112

1213

@@ -121,14 +122,23 @@ def simulate_ctd(
121122
# define output file for the simulation
122123
out_file = ctd_particleset.ParticleFile(name=out_path, outputdt=outputdt)
123124

124-
# execute simulation
125-
ctd_particleset.execute(
126-
[_sample_salinity, _sample_temperature, _ctd_cast],
127-
endtime=fieldset_endtime,
128-
dt=DT,
129-
verbose_progress=False,
130-
output_file=out_file,
131-
)
125+
# filter out Parcels logging messages
126+
for handler in external_logger.handlers:
127+
handler.addFilter(Filter())
128+
129+
# try/finally to ensure filter is always removed even if .execute fails (to avoid filter being appled universally)
130+
try:
131+
ctd_particleset.execute(
132+
[_sample_salinity, _sample_temperature, _ctd_cast],
133+
endtime=fieldset_endtime,
134+
dt=DT,
135+
verbose_progress=False,
136+
output_file=out_file,
137+
)
138+
139+
finally:
140+
for handler in external_logger.handlers:
141+
handler.removeFilter(handler.filters[0])
132142

133143
# there should be no particles left, as they delete themselves when they resurface
134144
if len(ctd_particleset.particledata) != 0:

src/virtualship/instruments/ctd_bgc.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import numpy as np
88
from parcels import FieldSet, JITParticle, ParticleSet, Variable
99

10+
from ..log_filter import Filter, external_logger
1011
from ..spacetime import Spacetime
1112

1213

@@ -127,14 +128,23 @@ def simulate_ctd_bgc(
127128
# define output file for the simulation
128129
out_file = ctd_bgc_particleset.ParticleFile(name=out_path, outputdt=outputdt)
129130

130-
# execute simulation
131-
ctd_bgc_particleset.execute(
132-
[_sample_o2, _sample_chlorophyll, _ctd_bgc_cast],
133-
endtime=fieldset_endtime,
134-
dt=DT,
135-
verbose_progress=False,
136-
output_file=out_file,
137-
)
131+
# filter out Parcels logging messages
132+
for handler in external_logger.handlers:
133+
handler.addFilter(Filter())
134+
135+
# try/finally to ensure filter is always removed even if .execute fails (to avoid filter being appled universally)
136+
try:
137+
ctd_bgc_particleset.execute(
138+
[_sample_o2, _sample_chlorophyll, _ctd_bgc_cast],
139+
endtime=fieldset_endtime,
140+
dt=DT,
141+
verbose_progress=False,
142+
output_file=out_file,
143+
)
144+
145+
finally:
146+
for handler in external_logger.handlers:
147+
handler.removeFilter(handler.filters[0])
138148

139149
# there should be no particles left, as they delete themselves when they resurface
140150
if len(ctd_bgc_particleset.particledata) != 0:

src/virtualship/instruments/drifter.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import numpy as np
88
from parcels import AdvectionRK4, FieldSet, JITParticle, ParticleSet, Variable
99

10+
from ..log_filter import Filter, external_logger
1011
from ..spacetime import Spacetime
1112

1213

@@ -95,14 +96,24 @@ def simulate_drifters(
9596
else:
9697
actual_endtime = np.timedelta64(endtime)
9798

98-
# execute simulation
99-
drifter_particleset.execute(
100-
[AdvectionRK4, _sample_temperature, _check_lifetime],
101-
endtime=actual_endtime,
102-
dt=dt,
103-
output_file=out_file,
104-
verbose_progress=True,
105-
)
99+
# filter out Parcels logging messages
100+
for handler in external_logger.handlers:
101+
handler.addFilter(Filter())
102+
103+
# try/finally to ensure filter is always removed even if .execute fails (to avoid filter being appled universally)
104+
try:
105+
# execute simulation
106+
drifter_particleset.execute(
107+
[AdvectionRK4, _sample_temperature, _check_lifetime],
108+
endtime=actual_endtime,
109+
dt=dt,
110+
output_file=out_file,
111+
verbose_progress=False,
112+
)
113+
114+
finally:
115+
for handler in external_logger.handlers:
116+
handler.removeFilter(handler.filters[0])
106117

107118
# if there are more particles left than the number of drifters with an indefinite endtime, warn the user
108119
if len(drifter_particleset.particledata) > len(
Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
"""Ship salinity and temperature."""
22

3-
import logging
43
from pathlib import Path
54

65
import numpy as np
76
from parcels import FieldSet, ParticleSet, ScipyParticle, Variable
87

9-
from ..log_filter import DuplicateFilter
8+
from ..log_filter import Filter, external_logger
109
from ..spacetime import Spacetime
1110

1211
# we specifically use ScipyParticle because we have many small calls to execute
@@ -34,7 +33,6 @@ def simulate_ship_underwater_st(
3433
out_path: str | Path,
3534
depth: float,
3635
sample_points: list[Spacetime],
37-
log_filter: bool = True,
3836
) -> None:
3937
"""
4038
Use Parcels to simulate underway data, measuring salinity and temperature at the given depth along the ship track in a fieldset.
@@ -43,7 +41,6 @@ def simulate_ship_underwater_st(
4341
:param out_path: The path to write the results to.
4442
:param depth: The depth at which to measure. 0 is water surface, negative is into the water.
4543
:param sample_points: The places and times to sample at.
46-
:param log_filter: Whether to filter duplicate log messages (defaults to True). This is a bit of a hack, but it works and could be removed if changed in Parcels.
4744
"""
4845
sample_points.sort(key=lambda p: p.time)
4946

@@ -60,33 +57,31 @@ def simulate_ship_underwater_st(
6057
# outputdt set to infinie as we want to just want to write at the end of every call to 'execute'
6158
out_file = particleset.ParticleFile(name=out_path, outputdt=np.inf)
6259

63-
# whether to filter parcels duplicate log messages
64-
if log_filter:
65-
external_logger = logging.getLogger("parcels.tools.loggers")
66-
for handler in external_logger.handlers:
67-
handler.addFilter(DuplicateFilter())
68-
69-
# iterate over each point, manually set lat lon time, then
70-
# execute the particle set for one step, performing one set of measurement
71-
for point in sample_points:
72-
particleset.lon_nextloop[:] = point.location.lon
73-
particleset.lat_nextloop[:] = point.location.lat
74-
particleset.time_nextloop[:] = fieldset.time_origin.reltime(
75-
np.datetime64(point.time)
76-
)
77-
78-
# perform one step using the particleset
79-
# dt and runtime are set so exactly one step is made.
80-
particleset.execute(
81-
[_sample_salinity, _sample_temperature],
82-
dt=1,
83-
runtime=1,
84-
verbose_progress=False,
85-
output_file=out_file,
86-
)
87-
88-
# turn off log filter after .execute(), to prevent being applied universally to all loggers
89-
# separate if statement from above to prevent error if log_filter is False
90-
if log_filter:
60+
# filter out Parcels logging messages
61+
for handler in external_logger.handlers:
62+
handler.addFilter(Filter())
63+
64+
# try/finally to ensure filter is always removed even if .execute fails (to avoid filter being appled universally)
65+
try:
66+
# iterate over each point, manually set lat lon time, then
67+
# execute the particle set for one step, performing one set of measurement
68+
for point in sample_points:
69+
particleset.lon_nextloop[:] = point.location.lon
70+
particleset.lat_nextloop[:] = point.location.lat
71+
particleset.time_nextloop[:] = fieldset.time_origin.reltime(
72+
np.datetime64(point.time)
73+
)
74+
75+
# perform one step using the particleset
76+
# dt and runtime are set so exactly one step is made.
77+
particleset.execute(
78+
[_sample_salinity, _sample_temperature],
79+
dt=1,
80+
runtime=1,
81+
verbose_progress=False,
82+
output_file=out_file,
83+
)
84+
85+
finally:
9186
for handler in external_logger.handlers:
9287
handler.removeFilter(handler.filters[0])

src/virtualship/instruments/xbt.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import numpy as np
88
from parcels import FieldSet, JITParticle, ParticleSet, Variable
99

10+
from ..log_filter import Filter, external_logger
1011
from ..spacetime import Spacetime
1112

1213

@@ -125,14 +126,24 @@ def simulate_xbt(
125126
# define output file for the simulation
126127
out_file = xbt_particleset.ParticleFile(name=out_path, outputdt=outputdt)
127128

128-
# execute simulation
129-
xbt_particleset.execute(
130-
[_sample_temperature, _xbt_cast],
131-
endtime=fieldset_endtime,
132-
dt=DT,
133-
verbose_progress=False,
134-
output_file=out_file,
135-
)
129+
# filter out Parcels logging messages
130+
for handler in external_logger.handlers:
131+
handler.addFilter(Filter())
132+
133+
# try/finally to ensure filter is always removed even if .execute fails (to avoid filter being appled universally)
134+
try:
135+
# execute simulation
136+
xbt_particleset.execute(
137+
[_sample_temperature, _xbt_cast],
138+
endtime=fieldset_endtime,
139+
dt=DT,
140+
verbose_progress=False,
141+
output_file=out_file,
142+
)
143+
144+
finally:
145+
for handler in external_logger.handlers:
146+
handler.removeFilter(handler.filters[0])
136147

137148
# there should be no particles left, as they delete themselves when they finish profiling
138149
if len(xbt_particleset.particledata) != 0:

0 commit comments

Comments
 (0)