Skip to content

Commit 1e68ddd

Browse files
committed
add class to evalute c++ density functions from picmi meta-data output
1 parent dc2bf77 commit 1e68ddd

File tree

2 files changed

+144
-1
lines changed

2 files changed

+144
-1
lines changed

lib/python/picongpu/extra/utils/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
from .memory_calculator import MemoryCalculator
33
from .field_ionization import FieldIonization
44
from . import FLYonPICRateCalculationReference
5+
from picmi_density_reader import cpp_fct_reader
56

6-
__all__ = ["FindTime", "MemoryCalculator", "FieldIonization", "FLYonPICRateCalculationReference"]
7+
__all__ = ["FindTime", "MemoryCalculator", "FieldIonization", "FLYonPICRateCalculationReference", "cpp_fct_reader"]
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import numpy as np
2+
import matplotlib.pyplot as plt
3+
import sympy
4+
import json
5+
6+
7+
class cpp_fct_reader:
8+
"""class that alllows to evalute the PIConGPU free density
9+
from a c++ code string
10+
"""
11+
12+
def __init__(self, s, debug=False):
13+
"""constructor
14+
s ... string from pypicongpu.json file
15+
debug ... bool for debug output
16+
"""
17+
self.debug = debug
18+
self.cpp_code = s.replace("\n", " ")
19+
self.cpp_code = self.inital_clean(self.cpp_code)
20+
21+
def evaluate(self, x, symbol="y"):
22+
"""evalute expression
23+
x ... float: position [m] where to evaluate the density
24+
symbol ... string: symbol to replace (default: 'y')
25+
"""
26+
return self.inner_evaluate(self.cpp_code, x, symbol=symbol) + 0.0 # add float for cast
27+
28+
def test_for_cases(self, s):
29+
"""internal method checking for c++ cases
30+
s ... string to check
31+
"""
32+
index_first_q = s.find("?")
33+
index_first_c = s.find(":")
34+
if index_first_q == -1 and index_first_c == -1:
35+
return False
36+
else:
37+
return True
38+
39+
def inner_evaluate(self, s, x, symbol):
40+
"""evalute string - this is a recursevly called internal method
41+
s ... string code
42+
x ... float value to evalute density at
43+
symbol ... string symbol to replace
44+
"""
45+
if self.test_for_cases(s):
46+
if self.debug:
47+
print("CASES")
48+
return self.eval_cases(s, x, symbol)
49+
50+
else:
51+
s_sym = sympy.parsing.sympy_parser.parse_expr(s)
52+
res = s_sym.subs(symbol, x)
53+
if self.debug:
54+
print("eval:", res)
55+
return res
56+
57+
def eval_cases(self, s, x, symbol):
58+
"""handle c++ cases o form cond ? case1 ? case 2
59+
s ... string code
60+
x ... float value to evalute
61+
symbol ... symbol to replace in string s
62+
"""
63+
if self.debug:
64+
print("-->", s)
65+
s = self.clean_substring(s)
66+
if self.debug:
67+
print("==>", s)
68+
index_first_q = s.find("?")
69+
index_first_c = s.find(":") # this assumes that there are no cases in the first branch
70+
71+
condition = s[0:index_first_q]
72+
case1 = s[index_first_q + 1 : index_first_c]
73+
case2 = s[index_first_c + 1 :]
74+
75+
if self.debug:
76+
print("if")
77+
print(condition)
78+
print("do")
79+
print(case1)
80+
print("else")
81+
print(case2)
82+
print("fi")
83+
84+
if self.inner_evaluate(condition, x, symbol):
85+
return self.inner_evaluate(case1, x, symbol)
86+
else:
87+
return self.inner_evaluate(case2, x, symbol)
88+
89+
def inital_clean(self, s):
90+
"""initally clean string from c++/picongpu methods
91+
making it readably for sympy
92+
s ... string
93+
"""
94+
if self.debug:
95+
print("clean before:", s)
96+
s = s.replace("^", "**")
97+
s = s.replace("pmacc::math::exp", "exp")
98+
s = s.replace("pmacc::math::pow", "pow")
99+
if self.debug:
100+
print("clean after:", s)
101+
return s
102+
103+
def clean_substring(self, s):
104+
"""clean any substrings from cases from paraneties and whitespace
105+
s ... string
106+
"""
107+
while s[0].isspace():
108+
s = s[1:]
109+
110+
while s[-1].isspace():
111+
s = s[:-1]
112+
113+
if s[0] == "(" and s[-1] == ")":
114+
s = s[1:-1]
115+
return s
116+
117+
118+
if __name__ == "__main__":
119+
# load pypicongpu.json, convert json to dict and extract equation for density
120+
file = open("pypicongpu.json")
121+
sim_dict = json.load(file)
122+
density_fct_str = sim_dict["species_initmanager"]["operations"]["simple_density"][0]["profile"]["data"][
123+
"function_body"
124+
]
125+
126+
# create cpp_fct_reader class for later evaluation
127+
reader = cpp_fct_reader(density_fct_str)
128+
129+
# define positions where to evaluate the density
130+
x_array = np.linspace(0.0, 5.0e-3, 1000)
131+
n_array = np.zeros_like(x_array)
132+
133+
# evalute density
134+
for i, x in enumerate(x_array):
135+
n_array[i] = reader.evaluate(x)
136+
137+
# plot density
138+
plt.plot(x_array, n_array)
139+
plt.xlabel(r"$y \, \mathrm{[m]}$")
140+
plt.xlabel(r"$n \, \mathrm{[m^-3]}$")
141+
plt.yscale("log")
142+
plt.show()

0 commit comments

Comments
 (0)