Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Time-Averaged Field Diagnostics #5285

Merged
merged 26 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8dbde7d
skeleton sprinkled around
RevathiJambunathan Sep 12, 2024
2707593
Add time step interval logic
n01r Sep 12, 2024
a1b21d2
adding diag type to constructor and moving
RevathiJambunathan Sep 13, 2024
0aee250
Add summation of MultiFabs
n01r Sep 13, 2024
1fef795
Set time averaging options in ParmParse
n01r Sep 14, 2024
17d7620
Continue averaging period logic
n01r Sep 17, 2024
c4b1775
Add docs and scope code in if envs
n01r Sep 18, 2024
d4cb9ae
Implement clang-tidy fixes
n01r Sep 18, 2024
ac216cc
Add time averaged diags to laser ion example
n01r Sep 19, 2024
66adf7f
Add more warnings and user info
n01r Sep 20, 2024
1bb3c7b
Add PICMI functionality
n01r Sep 20, 2024
c5f2e73
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 20, 2024
07e9b0e
Add and fix docs
n01r Sep 21, 2024
ea09ac2
Add abort for adaptive time-stepping
n01r Sep 24, 2024
98065f4
Update inputs_test_2d_laser_ion_acc for Ex
n01r Oct 2, 2024
171ce3c
Added automated test based on LPI example
n01r Oct 18, 2024
13cd6e4
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 18, 2024
22bc9a9
Unify CI test for regression and TimeAvgDiag
n01r Oct 18, 2024
0a46605
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 18, 2024
7c629d8
Apply suggestions from code review
n01r Oct 21, 2024
8a26f22
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 21, 2024
83ae4b9
Fix CI tests (missing `output_format` argument)
EZoni Oct 22, 2024
6ac6212
Merge `ECP-WarpX:development` into `RevathiJambunathan:time_averaged_…
EZoni Oct 22, 2024
8b10e46
Apply suggestions from code review
n01r Oct 22, 2024
b9fc39e
Update comment in Diagnostics.cpp
n01r Oct 22, 2024
fa03561
Update relative tolerance to stricter value
n01r Oct 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 51 additions & 4 deletions Docs/source/usage/parameters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2633,10 +2633,11 @@ Diagnostics and output
In-situ visualization
^^^^^^^^^^^^^^^^^^^^^

WarpX has four types of diagnostics:
``FullDiagnostics`` consist in dumps of fields and particles at given iterations,
``BackTransformedDiagnostics`` are used when running a simulation in a boosted frame, to reconstruct output data to the lab frame,
``BoundaryScrapingDiagnostics`` are used to collect the particles that are absorbed at the boundary, throughout the simulation, and
WarpX has five types of diagnostics:
``Full`` diagnostics consist in dumps of fields and particles at given iterations,
``TimeAveraged`` diagnostics only allow field data, which they output after averaging over a period of time,
``BackTransformed`` diagnostics are used when running a simulation in a boosted frame, to reconstruct output data to the lab frame,
``BoundaryScraping`` diagnostics are used to collect the particles that are absorbed at the boundary, throughout the simulation, and
``ReducedDiags`` allow the user to compute some reduced quantity (particle temperature, max of a field) and write a small amount of data to text files.
Similar to what is done for physical species, WarpX has a class Diagnostics that allows users to initialize different diagnostics, each of them with different fields, resolution and period.
This currently applies to standard diagnostics, but should be extended to back-transformed diagnostics and reduced diagnostics (and others) in a near future.
Expand Down Expand Up @@ -2882,12 +2883,58 @@ In-situ capabilities can be used by turning on Sensei or Ascent (provided they a
* ``warpx.mffile_nstreams`` (`int`) optional (default `4`)
Limit the number of concurrent readers per file.


.. _running-cpp-parameters-diagnostics-timeavg:

Time-Averaged Diagnostics
^^^^^^^^^^^^^^^^^^^^^^^^^

``TimeAveraged`` diagnostics are a special type of ``Full`` diagnostics that allows for the output of time-averaged field data.
This type of diagnostics can be created using ``<diag_name>.diag_type = TimeAveraged``.
We support only field data and related options from the list at `Full Diagnostics`_.

.. note::

As with ``Full`` diagnostics, ``TimeAveraged`` diagnostics output the initial **instantaneous** conditions of the selected fields on step 0 (unless more specific output intervals exclude output for step 0).

In addition, ``TimeAveraged`` diagnostic options include:

* ``<diag_name>.time_average_mode`` (`string`, default `none`)
Describes the operating mode for time averaged field output.

* ``none`` for no averaging (instantaneous fields)

* ``fixed_start`` for a diagnostic that averages all fields between the current output step and a fixed point in time

* ``dynamic_start`` for a constant averaging period and output at different points in time (non-overlapping)

.. note::

To enable time-averaged field output with intervals tightly spaced enough for overlapping averaging periods,
please create additional instances of ``TimeAveraged`` diagnostics.

* ``<diag_name>.average_period_steps`` (`int`)
Configures the number of time steps in an averaging period.
Set this only in the ``dynamic_start`` mode and only if ``average_period_time`` has not already been set.
Will be ignored in the ``fixed_start`` mode (with warning).

* ``<diag_name>.average_period_time`` (`float`, in seconds)
Configures the time (SI units) in an averaging period.
Set this only in the ``dynamic_start`` mode and only if ``average_period_steps`` has not already been set.
Will be ignored in the ``fixed_start`` mode (with warning).

* ``<diag_name>.average_start_step`` (`int`)
Configures the time step at which time-averaging begins.
Set this only in the ``fixed_start`` mode.
Will be ignored in the ``dynamic_start`` mode (with warning).

.. _running-cpp-parameters-diagnostics-btd:

BackTransformed Diagnostics
^^^^^^^^^^^^^^^^^^^^^^^^^^^

``BackTransformed`` diag type are used when running a simulation in a boosted frame, to reconstruct output data to the lab frame. This option can be set using ``<diag_name>.diag_type = BackTransformed``. We support the following list of options from `Full Diagnostics`_

``<diag_name>.format``, ``<diag_name>.openpmd_backend``, ``<diag_name>.dump_rz_modes``, ``<diag_name>.file_prefix``, ``<diag_name>.diag_lo``, ``<diag_name>.diag_hi``, ``<diag_name>.write_species``, ``<diag_name>.species``.

Additional options for this diagnostic include:
Expand Down
2 changes: 2 additions & 0 deletions Docs/source/usage/python.rst
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ Diagnostics

.. autoclass:: pywarpx.picmi.FieldDiagnostic

.. autoclass:: pywarpx.picmi.TimeAveragedFieldDiagnostic

.. autoclass:: pywarpx.picmi.ElectrostaticFieldDiagnostic

.. autoclass:: pywarpx.picmi.Checkpoint
Expand Down
8 changes: 4 additions & 4 deletions Examples/Physics_applications/laser_ion/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ add_warpx_test(
2 # dims
2 # nprocs
inputs_test_2d_laser_ion_acc # inputs
analysis_default_openpmd_regression.py # analysis
diags/diag1/ # output
analysis_test_laser_ion.py # analysis
diags/diagInst/ # output
OFF # dependency
)

Expand All @@ -16,7 +16,7 @@ add_warpx_test(
2 # dims
2 # nprocs
inputs_test_2d_laser_ion_acc_picmi.py # inputs
analysis_default_openpmd_regression.py # analysis
diags/diag1/ # output
analysis_test_laser_ion.py # analysis
diags/diagInst/ # output
OFF # dependency
)
2 changes: 1 addition & 1 deletion Examples/Physics_applications/laser_ion/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Visualize
:alt: Particle densities for electrons (top), protons (middle), and electrons again in logarithmic scale (bottom).
:width: 80%

Particle densities for electrons (top), protons (middle), and electrons again in logarithmic scale (bottom).
Particle densities for electrons (top), protons (middle), and electrons again in logarithmic scale (bottom).

Particle density output illustrates the evolution of the target in time and space.
Logarithmic scales can help to identify where the target becomes transparent for the laser pulse (bottom panel in :numref:`fig-tnsa-densities` ).
Expand Down

This file was deleted.

85 changes: 85 additions & 0 deletions Examples/Physics_applications/laser_ion/analysis_test_laser_ion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/usr/bin/env python3

import os
import sys

import numpy as np
import openpmd_api as io

sys.path.insert(1, "../../../../warpx/Regression/Checksum/")
from checksumAPI import evaluate_checksum


def load_field_from_iteration(
series, iteration: int, field: str, coord: str = None
) -> np.ndarray:
"""Load iteration of field data from file."""

it = series.iterations[iteration]
field_obj = it.meshes[f"{field}"]

if field_obj.scalar:
field_data = field_obj[io.Mesh_Record_Component.SCALAR].load_chunk()
elif coord in [item[0] for item in list(field_obj.items())]:
field_data = field_obj[coord].load_chunk()
else:
raise Exception(
f"Specified coordinate: f{coord} is not available for field: f{field}."
)
series.flush()

return field_data


def compare_time_avg_with_instantaneous_diags(dir_inst: str, dir_avg: str):
"""Compare instantaneous data (multiple iterations averaged in post-processing) with in-situ averaged data."""

field = "E"
coord = "z"
avg_period_steps = 5
avg_output_step = 100

path_tpl_inst = f"{dir_inst}/openpmd_%T.h5"
path_tpl_avg = f"{dir_avg}/openpmd_%T.h5"

si = io.Series(path_tpl_inst, io.Access.read_only)
sa = io.Series(path_tpl_avg, io.Access.read_only)

ii0 = si.iterations[0]
fi0 = ii0.meshes[field][coord]
shape = fi0.shape

data_inst = np.zeros(shape)

for i in np.arange(avg_output_step - avg_period_steps + 1, avg_output_step + 1):
data_inst += load_field_from_iteration(si, i, field, coord)

data_inst = data_inst / avg_period_steps

data_avg = load_field_from_iteration(sa, avg_output_step, field, coord)

# Compare the data
if np.allclose(data_inst, data_avg, rtol=1e-12):
print("Test passed: actual data is close to expected data.")
else:
print("Test failed: actual data is not close to expected data.")
sys.exit(1)


if __name__ == "__main__":
# NOTE: works only in the example directory due to relative path import
# compare checksums
evaluate_checksum(
test_name=os.path.split(os.getcwd())[1],
output_file=sys.argv[1],
output_format="openpmd",
)

# TODO: implement intervals parser for PICMI that allows more complex output periods
test_name = os.path.split(os.getcwd())[1]
if "picmi" not in test_name:
# Functionality test for TimeAveragedDiagnostics
compare_time_avg_with_instantaneous_diags(
dir_inst=sys.argv[1],
dir_avg="diags/diagTimeAvg/",
)
Original file line number Diff line number Diff line change
Expand Up @@ -200,18 +200,32 @@ laser1.profile_focal_distance = 4.0e-6 # focal distance from the antenna [m]
#################################
# Diagnostics
#
diagnostics.diags_names = diag1 openPMDfw openPMDbw
diagnostics.diags_names = diagInst diagTimeAvg openPMDfw openPMDbw

diag1.intervals = 100
diag1.diag_type = Full
diag1.fields_to_plot = Ex Ey Ez Bx By Bz jx jy jz rho rho_electrons rho_hydrogen
# instantaneous field and particle diagnostic
diagInst.intervals = 100,96:100 # second interval only for CI testing the time-averaged diags
diagInst.diag_type = Full
diagInst.fields_to_plot = Ex Ey Ez Bx By Bz jx jy jz rho rho_electrons rho_hydrogen
# reduce resolution of output fields
diag1.coarsening_ratio = 4 4
diagInst.coarsening_ratio = 4 4
# demonstration of a spatial and momentum filter
diag1.electrons.plot_filter_function(t,x,y,z,ux,uy,uz) = (uz>=0) * (x<1.0e-6) * (x>-1.0e-6)
diag1.hydrogen.plot_filter_function(t,x,y,z,ux,uy,uz) = (uz>=0) * (x<1.0e-6) * (x>-1.0e-6)
diag1.format = openpmd
diag1.openpmd_backend = h5
diagInst.electrons.plot_filter_function(t,x,y,z,ux,uy,uz) = (uz>=0) * (x<1.0e-6) * (x>-1.0e-6)
diagInst.hydrogen.plot_filter_function(t,x,y,z,ux,uy,uz) = (uz>=0) * (x<1.0e-6) * (x>-1.0e-6)
diagInst.format = openpmd
diagInst.openpmd_backend = h5

# time-averaged particle and field diagnostic
diagTimeAvg.intervals = 100
diagTimeAvg.diag_type = TimeAveraged
diagTimeAvg.time_average_mode = dynamic_start
#diagTimeAvg.average_period_time = 2.67e-15 # period of 800 nm light waves
diagTimeAvg.average_period_steps = 5 # use only either `time` or `steps`
diagTimeAvg.write_species = 0
diagTimeAvg.fields_to_plot = Ex Ey Ez Bx By Bz jx jy jz rho rho_electrons rho_hydrogen
# reduce resolution of output fields
diagTimeAvg.coarsening_ratio = 4 4
diagTimeAvg.format = openpmd
diagTimeAvg.openpmd_backend = h5

openPMDfw.intervals = 100
openPMDfw.diag_type = Full
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@

# Diagnostics
particle_diag = picmi.ParticleDiagnostic(
name="diag1",
name="diagInst",
period=100,
warpx_format="openpmd",
warpx_openpmd_backend="h5",
Expand All @@ -153,7 +153,7 @@
for ncell_comp, cr in zip([nx, nz], coarsening_ratio):
ncell_field.append(int(ncell_comp / cr))
field_diag = picmi.FieldDiagnostic(
name="diag1",
name="diagInst",
grid=grid,
period=100,
number_of_cells=ncell_field,
Expand All @@ -162,6 +162,18 @@
warpx_openpmd_backend="h5",
)

field_time_avg_diag = picmi.TimeAveragedFieldDiagnostic(
name="diagTimeAvg",
grid=grid,
period=100,
number_of_cells=ncell_field,
data_list=["B", "E", "J", "rho", "rho_electrons", "rho_hydrogen"],
warpx_format="openpmd",
warpx_openpmd_backend="h5",
warpx_time_average_mode="dynamic_start",
warpx_average_period_time=2.67e-15,
)

particle_fw_diag = picmi.ParticleDiagnostic(
name="openPMDfw",
period=100,
Expand Down Expand Up @@ -292,6 +304,7 @@
# Add full diagnostics
sim.add_diagnostic(particle_diag)
sim.add_diagnostic(field_diag)
sim.add_diagnostic(field_time_avg_diag)
sim.add_diagnostic(particle_fw_diag)
sim.add_diagnostic(particle_bw_diag)
# Add reduced diagnostics
Expand Down
2 changes: 1 addition & 1 deletion Examples/Physics_applications/laser_ion/plot_2d.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ def visualize_particle_histogram_iteration(
"-d",
"--diag_dir",
type=str,
default="./diags/diag1",
default="./diags/diagInst",
help="Directory containing density and field diagnostics",
)
parser.add_argument(
Expand Down
51 changes: 51 additions & 0 deletions Python/pywarpx/picmi.py
Original file line number Diff line number Diff line change
Expand Up @@ -3271,6 +3271,57 @@ def diagnostic_initialize_inputs(self):
ElectrostaticFieldDiagnostic = FieldDiagnostic


class TimeAveragedFieldDiagnostic(FieldDiagnostic):
"""
See `Input Parameters <https://warpx.readthedocs.io/en/latest/usage/parameters.html>`__ for more information.

Parameters
----------
warpx_time_average_mode: str
Type of time averaging diagnostic
Supported values include ``"none"``, ``"fixed_start"``, and ``"dynamic_start"``

* ``"none"`` for no averaging (instantaneous fields)
* ``"fixed_start"`` for a diagnostic that averages all fields between the current output step and a fixed point in time
* ``"dynamic_start"`` for a constant averaging period and output at different points in time (non-overlapping)

warpx_average_period_steps: int, optional
Configures the number of time steps in an averaging period.
Set this only in the ``"dynamic_start"`` mode and only if ``warpx_average_period_time`` has not already been set.
Will be ignored in the ``"fixed_start"`` mode (with warning).

warpx_average_period_time: float, optional
Configures the time (SI units) in an averaging period.
Set this only in the ``"dynamic_start"`` mode and only if ``average_period_steps`` has not already been set.
Will be ignored in the ``"fixed_start"`` mode (with warning).

warpx_average_start_steps: int, optional
Configures the time step at which time-averaging begins.
Set this only in the ``"fixed_start"`` mode.
Will be ignored in the ``"dynamic_start"`` mode (with warning).
"""

def init(self, kw):
super().init(kw)
self.time_average_mode = kw.pop("warpx_time_average_mode", None)
self.average_period_steps = kw.pop("warpx_average_period_steps", None)
self.average_period_time = kw.pop("warpx_average_period_time", None)
self.average_start_step = kw.pop("warpx_average_start_step", None)

def diagnostic_initialize_inputs(self):
super().diagnostic_initialize_inputs()

self.diagnostic.set_or_replace_attr("diag_type", "TimeAveraged")

if "write_species" not in self.diagnostic.argvattrs:
self.diagnostic.write_species = False

self.diagnostic.time_average_mode = self.time_average_mode
self.diagnostic.average_period_steps = self.average_period_steps
self.diagnostic.average_period_time = self.average_period_time
self.diagnostic.average_start_step = self.average_start_step


class Checkpoint(picmistandard.base._ClassWithInit, WarpXDiagnosticBase):
"""
Sets up checkpointing of the simulation, allowing for later restarts
Expand Down
2 changes: 1 addition & 1 deletion Source/Diagnostics/BTDiagnostics.H
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class BTDiagnostics final : public Diagnostics
{
public:

BTDiagnostics (int i, const std::string& name);
BTDiagnostics (int i, const std::string& name, DiagTypes diag_type);

private:
/** Whether to plot raw (i.e., NOT cell-centered) fields */
Expand Down
4 changes: 2 additions & 2 deletions Source/Diagnostics/BTDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ namespace
constexpr int permission_flag_rwxrxrx = 0755;
}

BTDiagnostics::BTDiagnostics (int i, const std::string& name)
: Diagnostics{i, name},
BTDiagnostics::BTDiagnostics (int i, const std::string& name, DiagTypes diag_type)
: Diagnostics{i, name, diag_type},
m_cell_centered_data_name("BTD_cell_centered_data_" + name)
{
ReadParameters();
Expand Down
2 changes: 1 addition & 1 deletion Source/Diagnostics/BoundaryScrapingDiagnostics.H
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public:
* @param i index of diagnostics in MultiDiagnostics::alldiags
* @param name diagnostics name in the inputs file
*/
BoundaryScrapingDiagnostics (int i, const std::string& name);
BoundaryScrapingDiagnostics (int i, const std::string& name, DiagTypes diag_type);

private:
/** Read relevant parameters for BoundaryScraping */
Expand Down
4 changes: 2 additions & 2 deletions Source/Diagnostics/BoundaryScrapingDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@

using namespace amrex::literals;

BoundaryScrapingDiagnostics::BoundaryScrapingDiagnostics (int i, const std::string& name)
: Diagnostics{i, name}
BoundaryScrapingDiagnostics::BoundaryScrapingDiagnostics (int i, const std::string& name, DiagTypes diag_type)
: Diagnostics{i, name, diag_type}
{
ReadParameters();
}
Expand Down
Loading
Loading