import shutil
from pathlib import Path
from pprint import pprint
import matplotlib.pyplot as plt
from bluemira.base.file import get_bluemira_path, get_bluemira_root
from bluemira.base.logs import set_log_level
from bluemira.codes import plasmod
Configuring the PLASMOD solver
PLASMOD is one of the codes bluemira can use to compliment a reactor design. As with any of the external codes bluemira uses, a solver object is created. The solver object abstracts away most of the complexities of running different programs within bluemira.
Setting up
Logging
To enable debug logging run the below cell
set_log_level("DEBUG")
Binary Location
Firstly if plasmod is not on your system path, we need to provide the binary location to the solver.
if plasmod_binary := shutil.which("plasmod"):
PLASMOD_PATH = Path(plasmod_binary).parent
else:
PLASMOD_PATH = Path(Path(get_bluemira_root()).parent, "plasmod/bin")
binary = Path(PLASMOD_PATH, "plasmod").as_posix()
Creating the solver object
bluemira-plasmod parameter names have been mapped across where possible.
Some example parameters have been set here in new_params
before being converted into a bluemira configuration store.
These parameters mirror running the plasmod input demoH.i reference configuration
new_params = {
"A": 3.1,
"R_0": 9.002,
"I_p": 17.75e6,
"B_0": 5.855,
"V_p": -2500,
"v_burn": -1.0e6,
"kappa_95": 1.652,
"delta_95": 0.333,
"delta": 0.38491934960310104,
"kappa": 1.6969830041844367,
}
Some values are not linked into bluemira. These plasmod parameters can be set
directly in problem_settings.
H-factor is set here as input therefore we will force plasmod to
optimise to that H-factor.
problem_settings = {
"pfus_req": 0.0,
"pheat_max": 130.0,
"q_control": 130.0,
"Hfact": 1.1,
"i_modeltype": "GYROBOHM_1",
"i_equiltype": "Ip_sawtooth",
"i_pedestal": "SAARELMA",
}
There are also some model choices that can be set in problem_settings.
The available models with their options and explanations
can be seen by running the below snippet.
for var_name in dir(plasmod.mapping):
if "Model" in var_name and var_name != "Model":
model = getattr(plasmod.mapping, var_name)
model.info()
Finally the build_config dictionary collates the configuration settings for
the solver
build_config = {
"problem_settings": problem_settings,
"binary": binary,
"directory": get_bluemira_path("", subfolder="generated_data"),
}
Now we can create the solver object with the parameters and build configuration
solver = plasmod.Solver(params=new_params, build_config=build_config)
These few functions are helpers to simplify the remainder of the tutorial. The first shows a few of the output scalar values and the second plots a given profile.
def print_outputs(solver):
"""
Print plasmod scalars
"""
outputs = solver.plasmod_outputs()
print(f"Fusion power [MW]: {solver.params.P_fus.value_as('MW')}")
print(f"Additional heating power [MW]: {outputs.Paux / 1e6}")
print(f"Radiation power [MW]: {solver.params.P_rad.value_as('MW')}")
print(
f"Transport power across separatrix [MW]: {solver.params.P_sep.value_as('MW')}"
)
print(f"{solver.params.q_95}")
print(f"{solver.params.I_p}")
print(f"{solver.params.l_i}")
print(f"{solver.params.v_burn}")
print(f"{solver.params.Z_eff}")
print(f"H-factor [-]: {outputs.Hfact}")
print(
"Divertor challenging criterion (P_sep * Bt /(q95 * R0 * A)) [-]:"
f" {outputs.psepb_q95AR}"
)
print(
"H-mode operating regime f_LH = P_sep/P_LH [-]:"
f" {solver.params.P_sep.value / solver.params.P_LH.value}"
)
print(f"{solver.params.tau_e}")
print(f"Protium fraction [-]: {outputs.cprotium}")
print(f"Helium fraction [-]: {outputs.che}")
print(f"Xenon fraction [-]: {outputs.cxe}")
print(f"Argon fraction [-]: {outputs.car}")
def plot_profile(solver, profile, var_unit):
"""
Plot plasmod profile
"""
prof = solver.get_profile(profile)
x = solver.get_profile(plasmod.Profiles.x)
_, ax = plt.subplots()
ax.plot(x, prof)
ax.set(xlabel="x (-)", ylabel=profile.name + " (" + var_unit + ")")
ax.grid()
plt.show()
Running the solver
Very simply use the run method of the solver
solver.execute(plasmod.RunMode.RUN)
Using the results
Outputs can be accessed through 3 ways depending on the linking mechanism.
Through the
paramsattribute which contains all the bluemira linked parametersProfiles can be accessed through the
get_profilefunction, using a value form theplasmod.ProfileenumUnlinked plasmod parameters can be accessed through the
plasmod_outputsfunction
The list of available profiles can be seen by running the below cell. A good exercise would be to try showing a different profile in the plot.
print("Profiles")
pprint(list(plasmod.mapping.Profiles)) # noqa: T203
plot_profile(solver, plasmod.Profiles.Te, "keV")
print_outputs(solver)
Plotting the results
There is a default set of output profiles that can be plotted easily:
plasmod.plot_default_profiles(solver)
Rerunning with modified settings
Changing the transport model
solver.problem_settings["i_modeltype"] = plasmod.TransportModel.GYROBOHM_2
solver.execute(plasmod.RunMode.RUN)
print_outputs(solver)
Fixing fusion power to 2000 MW and safety factor q_95 to 3.5.
Plasmod calculates the additional heating power and the plasma current
solver.params.q_95.set_value(3.5, "Input 1")
solver.problem_settings["pfus_req"] = 2000.0
solver.problem_settings["i_equiltype"] = plasmod.EquilibriumModel.q95_sawtooth
solver.problem_settings["isawt"] = plasmod.SafetyProfileModel.SAWTEETH
solver.problem_settings["q_control"] = 50.0
solver.execute(plasmod.RunMode.RUN)
print_outputs(solver)
Setting heat flux on divertor target to 10 MW/m²
plasmod calculates the argon concentration to fulfill the constraint
solver.problem_settings["qdivt_sup"] = 10.0
solver.execute(plasmod.RunMode.RUN)
print_outputs(solver)
Changing the mapping sending or receiving
The mapping can be changed on a given parameter or set of parameters.
Notice how the value of q_95 doesn’t change in the output,
even though its value has in the parameter (the previous value of 3.5 is used).
solver.modify_mappings({"q_95": {"send": False}})
solver.params.q_95.set_value(5, "Input 2")
solver.execute(plasmod.RunMode.RUN)
print_outputs(solver)
print("\nq_95 value history\n")
for hist in solver.params.q_95.history():
print(hist)