osiris-utils 1.1.10a0__py3-none-any.whl → 1.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. benchmarks/benchmark_hdf5_io.py +46 -0
  2. benchmarks/benchmark_load_all.py +54 -0
  3. docs/source/_static/Imagem1.png +0 -0
  4. docs/source/_static/custom.css +24 -8
  5. docs/source/api/decks.rst +48 -0
  6. docs/source/api/postprocess.rst +72 -8
  7. docs/source/api/sim_diag.rst +9 -7
  8. docs/source/api/utilities.rst +6 -6
  9. docs/source/conf.py +28 -8
  10. docs/source/examples/example_Derivatives.md +78 -0
  11. docs/source/examples/example_FFT.md +152 -0
  12. docs/source/examples/example_InputDeck.md +149 -0
  13. docs/source/examples/example_Simulation_Diagnostic.md +213 -0
  14. docs/source/examples/quick_start.md +51 -0
  15. docs/source/examples.rst +14 -0
  16. docs/source/index.rst +8 -0
  17. examples/edited-deck.1d +1 -1
  18. examples/example_Derivatives.ipynb +24 -36
  19. examples/example_FFT.ipynb +44 -23
  20. examples/example_InputDeck.ipynb +24 -277
  21. examples/example_Simulation_Diagnostic.ipynb +27 -17
  22. examples/quick_start.ipynb +17 -1
  23. osiris_utils/__init__.py +10 -6
  24. osiris_utils/cli/__init__.py +6 -0
  25. osiris_utils/cli/__main__.py +85 -0
  26. osiris_utils/cli/export.py +199 -0
  27. osiris_utils/cli/info.py +156 -0
  28. osiris_utils/cli/plot.py +189 -0
  29. osiris_utils/cli/validate.py +247 -0
  30. osiris_utils/data/__init__.py +15 -0
  31. osiris_utils/data/data.py +41 -171
  32. osiris_utils/data/diagnostic.py +285 -274
  33. osiris_utils/data/simulation.py +20 -13
  34. osiris_utils/decks/__init__.py +4 -0
  35. osiris_utils/decks/decks.py +83 -8
  36. osiris_utils/decks/species.py +12 -9
  37. osiris_utils/postprocessing/__init__.py +28 -0
  38. osiris_utils/postprocessing/derivative.py +317 -106
  39. osiris_utils/postprocessing/fft.py +135 -24
  40. osiris_utils/postprocessing/field_centering.py +28 -14
  41. osiris_utils/postprocessing/heatflux_correction.py +39 -18
  42. osiris_utils/postprocessing/mft.py +10 -2
  43. osiris_utils/postprocessing/postprocess.py +8 -5
  44. osiris_utils/postprocessing/pressure_correction.py +29 -17
  45. osiris_utils/utils.py +26 -17
  46. osiris_utils/vis/__init__.py +3 -0
  47. osiris_utils/vis/plot3d.py +148 -0
  48. {osiris_utils-1.1.10a0.dist-info → osiris_utils-1.2.1.dist-info}/METADATA +61 -7
  49. {osiris_utils-1.1.10a0.dist-info → osiris_utils-1.2.1.dist-info}/RECORD +53 -35
  50. {osiris_utils-1.1.10a0.dist-info → osiris_utils-1.2.1.dist-info}/WHEEL +1 -1
  51. osiris_utils-1.2.1.dist-info/entry_points.txt +2 -0
  52. {osiris_utils-1.1.10a0.dist-info → osiris_utils-1.2.1.dist-info}/top_level.txt +1 -0
  53. osiris_utils/postprocessing/mft_for_gridfile.py +0 -55
  54. {osiris_utils-1.1.10a0.dist-info → osiris_utils-1.2.1.dist-info}/licenses/LICENSE.txt +0 -0
@@ -0,0 +1,152 @@
1
+ # FFT Analysis
2
+
3
+ ```python
4
+ %load_ext autoreload
5
+ %autoreload 2
6
+ ```
7
+
8
+
9
+ ```python
10
+ import matplotlib.pyplot as plt
11
+ import numpy as np
12
+ from matplotlib.colors import LogNorm
13
+
14
+ import osiris_utils as ou
15
+ ```
16
+
17
+ ## Initialize an object of the Simulation class
18
+ - The Simulation class takes the `simulation_folder`, this is, the file where the input deck is located.
19
+ - It can also take the `species` when we want to use quantities related to a specific species, such as velocities, charge, temperature, etc.
20
+
21
+
22
+ ```python
23
+ # In this case, since we only want to study the electric field, no species is needed
24
+ sim = ou.Simulation(input_deck_path="example_data/thermal.1d")
25
+ ```
26
+
27
+ The `Simulation` object acts as a container for the simulation diagnostics, that can be easily accessed from the `Simulation` object using a dictionary-like syntax
28
+ In this case, to access the component of the electric field in the z direction, we can use `sim['e3']`. This is a `Diagnostic` object, that loads the data as requested using a data generator, using indices to choose the time step - a lazy loading approach.
29
+
30
+
31
+ ```python
32
+ # This is a Diagnostic object
33
+ sim["e3"]
34
+
35
+ # This is also a Diagnostic object but initialized directly and not through the Simulation object
36
+ e3 = ou.Diagnostic(simulation_folder="example_data")
37
+ e3.get_quantity("e3")
38
+
39
+ print("sim['e3'] class:", sim["e3"].__class__)
40
+ print("e3 class:", e3.__class__)
41
+ ```
42
+
43
+ The data of the diagnostic at a given index can be accessed by indexing the iteration number, e.g. `sim['e3'][0]` will return the electric field in the z direction at the first time step. The data is not stored in memory, but loaded from the file when requested, using a data generator (lazy loading). For example, to plot the electric field in the z direction at the 100th time step, we can use `sim['e3'][100]`, and use the other attributes of the `Diagnostic` object to get the time step, the grid, labels, etc.
44
+
45
+
46
+ ```python
47
+ plt.figure(figsize=(8, 3))
48
+ plt.plot(sim["e3"].x, sim["e3"][100])
49
+ plt.xlabel(sim["e3"].axis[0]["plot_label"])
50
+ plt.title(rf"${sim['e3'].label}$ @ $t = {sim['e3'].time(100)[0]:.2f}$ $[{sim['e3'].tunits}]$")
51
+ plt.show()
52
+ ```
53
+
54
+ If we want to load the data of the diagnostic at all time steps, we can use the `load_all()` method of the `Diagnostic` object, e.g. `sim['e3'].load_all()` will load the electric field in the z direction at all time steps and store it in memory. This is useful when we want to do the FFT of the data in the time domain, for example. Using the method `unload()` we can clear the data from memory.
55
+
56
+
57
+ ```python
58
+ sim["e3"].load_all()
59
+ print(sim["e3"].data.shape)
60
+
61
+ e3.load_all()
62
+ print(e3.data.shape)
63
+
64
+ np.isclose(sim["e3"].data, e3.data).all()
65
+
66
+ sim["e3"].unload()
67
+ e3.unload()
68
+ ```
69
+
70
+ Now we are going to use a post process on the simulation data and on the diagnostic. We can think of a `PostProcess` as an operator over the `Diagnostic`'s of the `Simulation`. We define it on the simulation, and to access a post-processed quantity of the simulation, we use the same dictionary-like syntax, e.g. `sim_fft["e3]` to access the FFT of the electric field in the z direction.
71
+
72
+ The data is loaded in memory when requested, using a data generator, and can be accessed using the same indexing syntax, e.g. `sim_fft["e3"][0]` to access the FFT of the electric field in the z direction at the first time step. The data can be loaded using the `load_all()` method, and cleared from memory using the `unload()` method.
73
+
74
+ The post-processing classes inherit from the `PostProcess` class and the `Diagnostic` class, and can be used as a `Diagnostic` object, with the same attributes and methods, ensuring that operations between diagnostics and post-processes are consistent.
75
+
76
+ Each diagnostic has a version to be applied to `Simulation` objects and another to be applied to `Diagnostic` objects. The first has the name `<name of post-process>`_Simulation, and the second has the name `<name of post-process>_Diagnostic`. For example, the post-process routines for Fast Fourier Transforms are called `FastFourierTransform_Diagnostic` and `FFT_Diagnostic`.
77
+
78
+
79
+ ```python
80
+ sim_fft = ou.FFT_Simulation(sim, (0, 1)) # "operator" FFT applied to Simulation object, axis 0 and 1
81
+
82
+ e3_fft = ou.FFT_Diagnostic(e3, (0, 1)) # "operator" FFT applied to Diagnostic object, axis 0 and 1
83
+ ```
84
+
85
+
86
+ ```python
87
+ sim_fft["e3"][100] # this will only return the kx axis, since we are only requesting the 100th time step of the FFT of e3
88
+
89
+ e3_fft[100] # this will return the kx and ky axis, since we are only requesting the 100th time step of the FFT of e3
90
+
91
+ np.isclose(sim_fft["e3"][100], e3_fft[100]).all() # They are the same!
92
+ ```
93
+
94
+ Since my goal is to plot the dispersion relation, I will need to use `.load_all()` to load all the time steps, and have access to the data in the frequency domain.
95
+
96
+
97
+ ```python
98
+ sim_fft["e3"].load_all()
99
+ e3_fft.load_all()
100
+
101
+ np.isclose(sim_fft["e3"].data, e3_fft.data).all() # They are the same!
102
+ ```
103
+
104
+ We can now plot!
105
+
106
+
107
+ ```python
108
+ plt.imshow(
109
+ sim_fft["e3"].data,
110
+ origin='lower',
111
+ norm=LogNorm(vmin=1e-7, vmax=0.01),
112
+ extent=(-sim_fft["e3"].kmax, sim_fft["e3"].kmax, -sim_fft["e3"].omega_max, sim_fft["e3"].omega_max),
113
+ aspect='auto',
114
+ cmap='gray',
115
+ )
116
+
117
+ # Plotting the dispersion relation
118
+ k = np.linspace(-e3_fft.kmax, e3_fft.kmax, num=512)
119
+ w = np.sqrt(1 + k**2)
120
+ plt.plot(k, w, label=r"$\omega^2 = \omega_p^2 + k^2c^2$", color='r', ls='-.')
121
+ plt.xlim(0, e3_fft.kmax)
122
+ plt.ylim(0, e3_fft.omega_max)
123
+
124
+ plt.xlabel(r"$k$ $[\omega_e/c]$")
125
+ plt.ylabel(r"$\omega$ $[\omega_e]$")
126
+ plt.legend()
127
+ plt.show()
128
+ ```
129
+
130
+
131
+ ```python
132
+ plt.imshow(
133
+ e3_fft.data,
134
+ origin='lower',
135
+ norm=LogNorm(vmin=1e-7, vmax=0.01),
136
+ extent=(-e3_fft.kmax, e3_fft.kmax, -e3_fft.omega_max, e3_fft.omega_max),
137
+ aspect='auto',
138
+ cmap='gray',
139
+ )
140
+
141
+ # Plotting the dispersion relation
142
+ k = np.linspace(-e3_fft.kmax, e3_fft.kmax, num=512)
143
+ w = np.sqrt(1 + k**2)
144
+ plt.plot(k, w, label=r"$\omega^2 = \omega_p^2 + k^2c^2$", color='r', ls='-.')
145
+ plt.xlim(0, e3_fft.kmax)
146
+ plt.ylim(0, e3_fft.omega_max)
147
+
148
+ plt.xlabel(r"$k$ $[\omega_e/c]$")
149
+ plt.ylabel(r"$\omega$ $[\omega_e]$")
150
+ plt.legend()
151
+ plt.show()
152
+ ```
@@ -0,0 +1,149 @@
1
+ ```python
2
+ %load_ext autoreload
3
+ %autoreload 2
4
+ ```
5
+
6
+
7
+ ```python
8
+ import osiris_utils as ou
9
+ ```
10
+
11
+ # Interface with Osiris Input Decks
12
+
13
+ For many tasks, it might be useful to read simulation parameters directly from the input deck, or algorithmically generate new Osiris input decks (e.g.to run large parameter scans).
14
+
15
+ The class `InputDeckIO` allows you to easily perform theses tasks.
16
+ You need to provide it two inputs:
17
+ - `filename`: path to OSIRIS input deck
18
+ - `verbose`: if `True` will print auxiliary information when parsing the input deck
19
+
20
+
21
+ ## Reading an input deck
22
+
23
+
24
+ ```python
25
+ deck = ou.InputDeckIO('example_data/thermal.1d', verbose=True)
26
+ ```
27
+
28
+ Not only does the object load the different module parameters, but also automatically determines other useful information for downstream tasks.
29
+
30
+ Examples include:
31
+ - `dim`: number of dimensions
32
+ - `n_species`: number of species
33
+ - `species`: dictionary with `Species` objects with relevant information (e.g. charge, rqm, etc.)
34
+
35
+
36
+ ```python
37
+ print("Simulation Dimensions:", deck.dim)
38
+ print("Number of species:", deck.n_species)
39
+ print("Species:", deck.species)
40
+ ```
41
+
42
+ `InputDeckIO` stores the state of the input deck in the parameter `self._sections`.
43
+
44
+ You should not access this value directly, instead you can use for example its getter function `self.sections` which returns a deepcopy.
45
+
46
+ The returned value corresponds to a list of pairs [section_name, dictionary] where the section_name matches the Osiris input deck section name and he dictionary contains the list of (key, values) for the parameters of the section.
47
+
48
+
49
+ ```python
50
+ print(deck.sections[0])
51
+ print(deck.sections[1])
52
+ print('Number of sections:', len(deck.sections))
53
+ ```
54
+
55
+ Another option is to query the object as if it is an iterator, where the key is the section name.
56
+
57
+ This will return a list of dictionaries, since multiple sections can have the same name (e.g. you might have multiple species).
58
+
59
+ Once again, a deepcopy is being returned so editing the returned values of the dictionaries will not change the original `InputDeckIO` object.
60
+
61
+
62
+ ```python
63
+ print(deck['simulation'])
64
+ print(deck['node_conf'])
65
+ ```
66
+
67
+ Finally you can also ask for a specific parameter directly with `get_param()`
68
+
69
+
70
+ ```python
71
+ # this one returns a list since there can be multiple sections with the same name
72
+ print(deck.get_param(section='simulation', param='random_seed')[0])
73
+ # which is equivalent to doing this
74
+ print(deck["simulation"][0]['random_seed'])
75
+ ```
76
+
77
+ ## Editing an input deck
78
+
79
+ To safely edit the value of a parameter in an input deck you can use `set_parameter()`.
80
+
81
+ **Note**: This re-writes the object values!
82
+
83
+
84
+
85
+ ```python
86
+ # edit a parameter already exists
87
+ print('Before', deck['simulation'])
88
+ deck.set_param('simulation', 'random_seed', value=42)
89
+ print('After', deck['simulation'])
90
+ ```
91
+
92
+
93
+ ```python
94
+ # add a parameter
95
+ print("Before", deck["simulation"])
96
+ deck.set_param("simulation", "new_parameter", value='HI', unexistent_ok=True)
97
+ print("After", deck["simulation"])
98
+ ```
99
+
100
+ And you can also delete the parameter using `delete_param()`.
101
+
102
+
103
+ ```python
104
+ # delete a parameter
105
+ print("Before", deck["simulation"])
106
+ deck.delete_param("simulation", "new_parameter")
107
+ print("After", deck["simulation"])
108
+ ```
109
+
110
+ Something slighlty more powerful, is the ability to edit a string in the whole input deck (use with care!)
111
+
112
+ This can be done for example to automatically change multiple parameter values that are shared / depend on an external quantity.
113
+
114
+ We can do this using the function `set_tag()`.
115
+
116
+ **Note**: This only works on the parameter values, not section names / parameter names.
117
+
118
+
119
+ ```python
120
+ # this a dummy example where we change some values to #tag#
121
+ print("Before", deck["simulation"])
122
+ deck.set_tag(
123
+ '42', # this has to be a string
124
+ '#tag#',
125
+ )
126
+ deck.set_tag(
127
+ '23:50:00', # this has to be a string
128
+ '#tag#',
129
+ )
130
+ print("After 1", deck["simulation"])
131
+
132
+ # and here we change both values at once
133
+ deck.set_tag('#tag#', "BOTH CHANGED")
134
+ print("After 2", deck["simulation"])
135
+ # There is a reason why wall_clock_limit has an extra "" done worry
136
+ # it is because it should be a string, while random_seed is an int!
137
+ # InputDeckIO handles these things for you
138
+ ```
139
+
140
+ ## Writing changes to file
141
+
142
+ Once you did your changes, you can simply generate a new input deck with `print_to_file()`.
143
+
144
+
145
+ ```python
146
+ deck.print_to_file("edited-deck.1d")
147
+ with open("edited-deck.1d") as f:
148
+ print(f.read())
149
+ ```
@@ -0,0 +1,213 @@
1
+ ```python
2
+ %load_ext autoreload
3
+ %autoreload 2
4
+ ```
5
+
6
+
7
+ ```python
8
+ import matplotlib.pyplot as plt
9
+ import numpy as np
10
+
11
+ import osiris_utils as ou
12
+ ```
13
+
14
+ # Example notebook for the `Simulation` and `Diagnostic` classes
15
+
16
+ ## `Simulation`
17
+ The `Simulation` class takes in:
18
+ - `simulation_folder`: the folder where the input deck is located
19
+ - `species`: the species in case we are interested in diagnostics that are species-specific (such as vfl1, T11, etc.)
20
+
21
+ Acts as a wrapper for the `Diagnostic` class, allowing for an easy access to diagnostics of the simulation using a **dictionary-like** syntax.
22
+
23
+ ## `Diagnostic`
24
+
25
+ The `Diagnostic` class takes in:
26
+ - `simulation_folder`: the folder where the input deck is located
27
+ - `species`: the species in case we are interested in diagnostics that are species-specific (such as vfl1, T11, etc.)
28
+
29
+
30
+ The `Diagnostic` class has the some relevant methods and properties that you should be aware of:
31
+ - `get_quantity`: "loads" the quantity. Accessing the diagnostic through the `Simulations` using the dictionary-like syntax this will be called automatically.
32
+ - once the quantity is loaded, you can access the data at a specific time through indexing (e.g. `diag['vfl1'][0]` for the first time step). This doesn't store the data in memory, only uses a data generator to access the data (lazy loading).
33
+ - `load_all`: loads all the time steps of the quantity, storing it in memory
34
+ - `unload`: unloads the data from memory
35
+ - `time`: the time steps of the quantity at a specific index
36
+ - Has operator overloading for the `+`, `-`, `*`, `/` operators, allowing for easy manipulation of the data, even when it is not loaded in memory and is accessed through indexing.
37
+
38
+
39
+ ```python
40
+ # sim is a Simulation object
41
+ sim = ou.Simulation(input_deck_path="example_data/thermal.1d")
42
+
43
+ print("Simulation:")
44
+ print(sim)
45
+ print(sim.__dict__.keys())
46
+ print(f"\n")
47
+
48
+ # e3 is a Diagnostic object
49
+ e3 = ou.Diagnostic(simulation_folder="example_data")
50
+ # now we need to load the data
51
+ e3.get_quantity("e3") # it knows the path there since OSIRIS always saves the data in the same way
52
+
53
+ print("Diagnostic: (e3)")
54
+ print(e3)
55
+ print(e3.__dict__.keys())
56
+ ```
57
+
58
+ If the diagnostic that you want to access if related to a species, you need to access it by using the species name as the first key.
59
+
60
+
61
+ ```python
62
+ # An electric field if not relatec to a species, so you can access it by doing:
63
+ sim["e3"]
64
+
65
+ # To access a species-related diagnostic, you can do:
66
+ sim["electrons"]["vfl1"]
67
+
68
+ # To see the available species of a simulation, you can do:
69
+ sim.species
70
+ ```
71
+
72
+ Notice that the `Diagnostic` class does not have `data` as the data is not saved in memory if `load_all` is not called. The data is accessed through the `__getitem__` method using indexing.
73
+
74
+ To get a `Diagnostic` from a `Simulation`, you can access it through the dictionary-like syntax, where the key is the name of the diagnostic (e.g. `sim['vfl1']` is a `Diagnostic` with the `vfl1` quantity).
75
+
76
+
77
+ ```python
78
+ sim["e3"] # This is equivalent to e3
79
+
80
+ print(sim["e3"])
81
+ print(sim["e3"].__dict__.keys())
82
+ ```
83
+
84
+ If we want to access the data of the diagnostic at a specific time, we can use the indexing syntax (e.g. `sim['vfl1'][0]` for the first time step, using a `Simulation`, or `e3[0]`, using the `Diagnostic` directly).
85
+
86
+
87
+ ```python
88
+ print(sim["e3"][100].shape) # nx
89
+ print(e3[100].shape) # nx
90
+
91
+ print("Are they the same?", np.isclose(sim["e3"][100], e3[100]).all())
92
+ ```
93
+
94
+ This is useful when we want to access a specific iteration of the diagnostic, without loading all the data in memory.
95
+
96
+ For a quick plot, we can even use the other attributes of the `Diagnostic` class, such as `time`, `x` for the axis, of `axis` for info about the axis.
97
+
98
+
99
+ ```python
100
+ plt.figure(figsize=(12, 4))
101
+ plt.subplot(121)
102
+ plt.plot(sim["e3"].x, sim["e3"][100], label="Simulation", c="tab:orange")
103
+ plt.title("Using Simulation")
104
+ plt.xlabel(sim["e3"].axis[0]["plot_label"])
105
+ plt.legend()
106
+
107
+ plt.subplot(122)
108
+ plt.plot(e3.x, e3[100], label="Diagnostic", c="tab:blue")
109
+ plt.title("Using Diagnostic")
110
+ plt.xlabel(e3.axis[0]["plot_label"])
111
+ plt.legend()
112
+ plt.show()
113
+ ```
114
+
115
+ If we want to load all the data in memory, we can use the `load_all` method, which will load all the data in memory. This is useful to study how diagnostics vary in time, for example.
116
+
117
+ The data is stored in the `data` attribute of the `Diagnostic` class.
118
+
119
+ If we want to unload the data from memory, we can use the `unload` method.
120
+
121
+ From now on, I'll only use `sim[diagnostic]` to show what can be done with `Diagnostic` objects, but remember that you can access the `Diagnostic` object directly if you want to use the `load_all`, `unload`, `time`, `x`, and `axis` attributes.
122
+
123
+
124
+ ```python
125
+ sim["e3"].load_all()
126
+
127
+ # now, the data is loaded in memory
128
+ print(sim["e3"].data.shape) # (n_steps, nx)
129
+
130
+ sim["e3"].unload()
131
+ ```
132
+
133
+ Since diagnostics have operator overloading, we can easily manipulate the data, even when it is not loaded in memory. For example, we can do `sim['vfl1'] + sim['e1']` to get the sum of the two diagnostics, or `sim['vfl1'] / sim['e1']` to get the division of the two diagnostics.
134
+
135
+ When operations are done, a new `Diagnostic` is created, with the new data. This new `Diagnostic` can be accessed through the indexing syntax, and the data can be loaded in memory using the `load_all` method.
136
+
137
+
138
+ ```python
139
+ sum_of_diag = sim["electrons"]["T11"] + sim["electrons"]["vfl1"]
140
+
141
+ print(sum_of_diag)
142
+ print(sum_of_diag.__dict__.keys())
143
+ ```
144
+
145
+ We can use the method of the `Diagnostic` class as usual, for example, `load_all`, and `unload`:
146
+
147
+
148
+ ```python
149
+ # we can load all the data at once
150
+ sum_of_diag.load_all()
151
+ print(sum_of_diag.data.shape) # (n_steps, nx)
152
+ sum_of_diag.unload()
153
+ ```
154
+
155
+ And accessing a specific time step of the new `Diagnostic` is done through indexing, as usual.
156
+
157
+
158
+ ```python
159
+ plt.figure(figsize=(12, 4))
160
+ plt.subplot(121)
161
+ plt.plot(sum_of_diag.x, sum_of_diag[100], label="T11 + vfl1", c="tab:orange")
162
+ plt.title("Diagnostic from sum of two diagnostics")
163
+ plt.xlabel(sum_of_diag.axis[0]["plot_label"])
164
+ plt.legend()
165
+ plt.show()
166
+ ```
167
+
168
+ This is really useful when we want a quantity that OSIRIS doesn't provide. For example, when the pressure is adiabatic, we can calculate the pressure using the formula `P = n * T`, where `P` is the pressure, `n` is the density, and `T` is the temperature. We can calculate the pressure using the formula `P = n * T` using the `Diagnostic` class, and then use the pressure to calculate the adiabatic index `gamma = 5/3` using the formula `gamma = P / (n * T)`.
169
+
170
+
171
+ ```python
172
+ nT = -1 * sim["electrons"]["T11"] * sim["electrons"]["charge"] # -1 because we are dealing with electrons
173
+ ```
174
+
175
+
176
+ ```python
177
+ plt.figure(figsize=(6, 4))
178
+ plt.plot(nT.x, nT[100], label=r"$n_eT_{11}$", c="tab:green")
179
+ plt.title(r"$n_eT_{11}$")
180
+ plt.xlabel(nT.axis[0]["plot_label"])
181
+ plt.legend()
182
+ plt.show()
183
+ ```
184
+
185
+ If the `load_all` method is called before the operations, the whole data will be stored in the new quantity.
186
+
187
+
188
+ ```python
189
+ sim["electrons"]["vfl1"].load_all()
190
+ sim["electrons"]["charge"].load_all()
191
+
192
+ v_times_charge = sim["electrons"]["vfl1"] * sim["electrons"]["charge"]
193
+
194
+ print(v_times_charge)
195
+ print(v_times_charge.__dict__.keys())
196
+ ```
197
+
198
+ The whole data is computed if stored in the diagnostics before the operations
199
+
200
+
201
+ ```python
202
+ print(v_times_charge.data.shape)
203
+ ```
204
+
205
+ And it can be unloaded from memory using the `unload` method.
206
+
207
+
208
+ ```python
209
+ v_times_charge.unload()
210
+
211
+ # print something that checks that data is none
212
+ print("Is `v_times_charge.data` None?", v_times_charge._data is None)
213
+ ```
@@ -0,0 +1,51 @@
1
+ # Quick Start Guide
2
+
3
+ ```python
4
+ %load_ext autoreload
5
+ %autoreload 2
6
+ ```
7
+
8
+
9
+ ```python
10
+ """
11
+ Quick demonstration of osiris_utils:
12
+ • opens an OSIRIS simulation directory
13
+ • plots Ez field at t = 2000
14
+ Run with: python examples/quick_start.py <PATH_TO_RUN>
15
+ """
16
+ ```
17
+
18
+
19
+ ```python
20
+ import sys
21
+ from pathlib import Path
22
+
23
+ import matplotlib.pyplot as plt
24
+
25
+ import osiris_utils as ou
26
+
27
+ ```
28
+
29
+
30
+ ```python
31
+ sim_path = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("example_data/thermal.1d")
32
+ sim = ou.Simulation(sim_path)
33
+ ```
34
+
35
+
36
+ ```python
37
+ # grab Ez diagnostic
38
+ ez = sim["e3"]
39
+ ```
40
+
41
+
42
+ ```python
43
+ # plot Ez field at iteration 220
44
+ plt.plot(ez.x, ez[220], label=f"${ez.label}$")
45
+ plt.title(rf"${ez.label}$ at t = {ez.time(220)[0]} $[{ez.time(220)[1]}]$")
46
+ plt.xlabel(ez.axis[0]["plot_label"])
47
+ plt.ylabel(f"${ez.units}$")
48
+ plt.legend()
49
+ plt.tight_layout()
50
+ plt.show()
51
+ ```
@@ -0,0 +1,14 @@
1
+ Examples
2
+ ========
3
+
4
+ This section contains examples and tutorials on how to use ``osiris_utils``.
5
+
6
+ .. toctree::
7
+ :maxdepth: 1
8
+ :caption: Tutorials:
9
+
10
+ examples/quick_start
11
+ examples/example_Simulation_Diagnostic
12
+ examples/example_InputDeck
13
+ examples/example_Derivatives
14
+ examples/example_FFT
docs/source/index.rst CHANGED
@@ -5,6 +5,13 @@ OSIRIS Utils Documentation
5
5
 
6
6
  .. include:: ../../README.rst
7
7
 
8
+ .. toctree::
9
+ :maxdepth: 2
10
+ :caption: Examples:
11
+ :hidden:
12
+
13
+ examples
14
+
8
15
  .. toctree::
9
16
  :maxdepth: 2
10
17
  :caption: Simulation and Diagnostic API:
@@ -24,6 +31,7 @@ OSIRIS Utils Documentation
24
31
  :caption: Utilities API:
25
32
  :hidden:
26
33
 
34
+ api/decks
27
35
  api/utilities
28
36
 
29
37
  .. toctree::
examples/edited-deck.1d CHANGED
@@ -55,7 +55,7 @@ diag_emf
55
55
  {
56
56
  ndump_fac = 1,
57
57
  ndump_fac_ene_int = 1,
58
- reports = "e1", "e2", "e3",
58
+ reports = "e3",
59
59
  }
60
60
 
61
61
  particles