"""Estimate 1D plasma profiles of density and temperature."""
from typing import Any
import numpy as np
from ...algorithm_class import Algorithm
from ...named_options import ProfileForm
from ...unit_handling import Unitfull, ureg, wraps_ufunc
from .density_peaking import calc_density_peaking, calc_effective_collisionality
from .numerical_profile_fits import evaluate_density_and_temperature_profile_fits
[docs]
@Algorithm.register_algorithm(
return_keys=[
"effective_collisionality",
"ion_density_peaking",
"electron_density_peaking",
"peak_electron_density",
"peak_fuel_ion_density",
"peak_electron_temp",
"peak_ion_temp",
"rho",
"electron_density_profile",
"fuel_ion_density_profile",
"electron_temp_profile",
"ion_temp_profile",
]
)
def calc_peaked_profiles(
average_electron_density: Unitfull,
average_electron_temp: Unitfull,
average_ion_temp: Unitfull,
ion_density_peaking_offset: Unitfull,
electron_density_peaking_offset: Unitfull,
temperature_peaking: Unitfull,
major_radius: Unitfull,
z_effective: Unitfull,
dilution: Unitfull,
beta_toroidal: Unitfull,
normalized_inverse_temp_scale_length: Unitfull,
density_profile_form: ProfileForm,
temp_profile_form: ProfileForm,
) -> tuple[Unitfull, ...]:
"""Calculate density peaking and the corresponding density and temperature profiles.
Args:
average_electron_density: :term:`glossary link<average_electron_density>`
average_electron_temp: :term:`glossary link<average_electron_temp>`
average_ion_temp: :term:`glossary link<average_ion_temp>`
ion_density_peaking_offset: :term:`glossary link<ion_density_peaking_offset>`
electron_density_peaking_offset: :term:`glossary link<electron_density_peaking_offset>`
temperature_peaking: :term:`glossary link<temperature_peaking>`
major_radius: :term:`glossary link<major_radius>`
z_effective: :term:`glossary link<z_effective>`
dilution: :term:`glossary link<dilution>`
beta_toroidal: :term:`glossary link<beta_toroidal>`
normalized_inverse_temp_scale_length: :term:`glossary link<normalized_inverse_temp_scale_length>`
density_profile_form: :term:`glossary link<density_profile_form>`
temp_profile_form: :term:`glossary link<temp_profile_form>`
Returns:
`effective_collisionality`, :term:`ion_density_peaking`, :term:`electron_density_peaking`, :term:`peak_electron_density`, :term:`peak_electron_temp`, :term:`peak_ion_temp`, :term:`rho`, :term:`electron_density_profile`, :term:`fuel_ion_density_profile`, :term:`electron_temp_profile`, :term:`ion_temp_profile`
"""
effective_collisionality = calc_effective_collisionality(average_electron_density, average_electron_temp, major_radius, z_effective)
ion_density_peaking = calc_density_peaking(effective_collisionality, beta_toroidal, nu_noffset=ion_density_peaking_offset)
electron_density_peaking = calc_density_peaking(effective_collisionality, beta_toroidal, nu_noffset=electron_density_peaking_offset)
peak_electron_density = average_electron_density * electron_density_peaking
peak_fuel_ion_density = average_electron_density * dilution * ion_density_peaking
peak_electron_temp = average_electron_temp * temperature_peaking
peak_ion_temp = average_ion_temp * temperature_peaking
# Calculate the total fusion power by estimating density and temperature profiles and
# using this to calculate fusion power profiles.
(rho, electron_density_profile, fuel_ion_density_profile, electron_temp_profile, ion_temp_profile) = calc_1D_plasma_profiles(
density_profile_form=density_profile_form,
temp_profile_form=temp_profile_form,
average_electron_density=average_electron_density,
average_electron_temp=average_electron_temp,
average_ion_temp=average_ion_temp,
electron_density_peaking=electron_density_peaking,
ion_density_peaking=ion_density_peaking,
temperature_peaking=temperature_peaking,
dilution=dilution,
normalized_inverse_temp_scale_length=normalized_inverse_temp_scale_length,
)
return (
effective_collisionality,
ion_density_peaking,
electron_density_peaking,
peak_electron_density,
peak_fuel_ion_density,
peak_electron_temp,
peak_ion_temp,
rho,
electron_density_profile,
fuel_ion_density_profile,
electron_temp_profile,
ion_temp_profile,
)
[docs]
@Algorithm.register_algorithm(
return_keys=["rho", "electron_density_profile", "fuel_ion_density_profile", "electron_temp_profile", "ion_temp_profile"]
)
@wraps_ufunc(
return_units=dict(
rho=ureg.dimensionless,
electron_density_profile=ureg.n19,
fuel_ion_density_profile=ureg.n19,
electron_temp_profile=ureg.keV,
ion_temp_profile=ureg.keV,
),
input_units=dict(
density_profile_form=None,
temp_profile_form=None,
average_electron_density=ureg.n19,
average_electron_temp=ureg.keV,
average_ion_temp=ureg.keV,
electron_density_peaking=ureg.dimensionless,
ion_density_peaking=ureg.dimensionless,
temperature_peaking=ureg.dimensionless,
dilution=ureg.dimensionless,
normalized_inverse_temp_scale_length=ureg.dimensionless,
n_points_for_confined_region_profiles=None,
),
output_core_dims=[("dim_rho",), ("dim_rho",), ("dim_rho",), ("dim_rho",), ("dim_rho",)],
)
def calc_1D_plasma_profiles(
density_profile_form: ProfileForm,
temp_profile_form: ProfileForm,
average_electron_density: float,
average_electron_temp: float,
average_ion_temp: float,
electron_density_peaking: float,
ion_density_peaking: float,
temperature_peaking: float,
dilution: float,
normalized_inverse_temp_scale_length: float,
n_points_for_confined_region_profiles: int = 50,
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
"""Estimate density and temperature profiles.
Args:
density_profile_form: :term:`<glossary link<density_profile_form>`
temp_profile_form: :term:`<glossary link<temp_profile_form>`
average_electron_density: [1e19 m^-3] :term:`glossary link<average_electron_density>`
average_electron_temp: [keV] :term:`glossary link<average_electron_temp>`
average_ion_temp: [keV] :term:`glossary link<average_ion_temp>`
electron_density_peaking: [~] :term:`glossary link<electron_density_peaking>`
ion_density_peaking: [~] :term:`glossary link<ion_density_peaking>`
temperature_peaking: [~] :term:`glossary link<temperature_peaking>`
dilution: dilution of main ions [~]
normalized_inverse_temp_scale_length: [~] :term:`glossary link<normalized_inverse_temp_scale_length>`
n_points_for_confined_region_profiles: number of points to return in profile
Returns:
:term:`rho` [~], :term:`electron_density_profile` [1e19 m^-3], fuel_ion_density_profile [1e19 m^-3], :term:`electron_temp_profile` [keV], :term:`ion_temp_profile` [keV]
"""
kwargs: dict[str, Any] = dict(
average_electron_density=average_electron_density,
average_electron_temp=average_electron_temp,
average_ion_temp=average_ion_temp,
electron_density_peaking=electron_density_peaking,
ion_density_peaking=ion_density_peaking,
temperature_peaking=temperature_peaking,
dilution=dilution,
npoints=n_points_for_confined_region_profiles,
)
electron_density_profiles, fuel_ion_density_profiles, electron_temp_profiles, ion_temp_profiles = dict(), dict(), dict(), dict()
(
rho_1,
electron_density_profiles[ProfileForm.analytic],
fuel_ion_density_profiles[ProfileForm.analytic],
electron_temp_profiles[ProfileForm.analytic],
ion_temp_profiles[ProfileForm.analytic],
) = calc_analytic_profiles(**kwargs)
(
rho_2,
electron_density_profiles[ProfileForm.prf],
fuel_ion_density_profiles[ProfileForm.prf],
electron_temp_profiles[ProfileForm.prf],
ion_temp_profiles[ProfileForm.prf],
) = calc_prf_profiles(**kwargs, normalized_inverse_temp_scale_length=normalized_inverse_temp_scale_length)
assert np.allclose(rho_1, rho_2)
return (
rho_1,
electron_density_profiles[density_profile_form],
fuel_ion_density_profiles[density_profile_form],
electron_temp_profiles[temp_profile_form],
ion_temp_profiles[temp_profile_form],
)
def calc_analytic_profiles(
average_electron_density: float,
average_electron_temp: float,
average_ion_temp: float,
electron_density_peaking: float,
ion_density_peaking: float,
temperature_peaking: float,
dilution: float,
npoints: int = 50,
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
"""Estimate density and temperature profiles using a simple analytic fit.
Args:
average_electron_density: [1e19 m^-3] :term:`glossary link<average_electron_density>`
average_electron_temp: [keV] :term:`glossary link<average_electron_temp>`
average_ion_temp: [keV] :term:`glossary link<average_ion_temp>`
electron_density_peaking: [~] :term:`glossary link<electron_density_peaking>`
ion_density_peaking: [~] :term:`glossary link<ion_density_peaking>`
temperature_peaking: [~] :term:`glossary link<temperature_peaking>`
dilution: dilution of main ions [~]
npoints: number of points to return in profile
Returns:
:term:`rho` [~], :term:`electron_density_profile` [1e19 m^-3], fuel_ion_density_profile [1e19 m^-3], :term:`electron_temp_profile` [keV], :term:`ion_temp_profile` [keV]
"""
rho = np.linspace(0, 1, num=npoints, endpoint=False)
electron_density_profile = average_electron_density * electron_density_peaking * ((1.0 - rho**2.0) ** (electron_density_peaking - 1.0))
fuel_ion_density_profile = (
average_electron_density * dilution * (ion_density_peaking) * ((1.0 - rho**2.0) ** (ion_density_peaking - 1.0))
)
electron_temp_profile = average_electron_temp * temperature_peaking * ((1.0 - rho**2.0) ** (temperature_peaking - 1.0))
ion_temp_profile = average_ion_temp * temperature_peaking * ((1.0 - rho**2.0) ** (temperature_peaking - 1.0))
return rho, electron_density_profile, fuel_ion_density_profile, electron_temp_profile, ion_temp_profile
def calc_prf_profiles(
average_electron_density: float,
average_electron_temp: float,
average_ion_temp: float,
electron_density_peaking: float,
ion_density_peaking: float,
temperature_peaking: float,
dilution: float,
normalized_inverse_temp_scale_length: float,
npoints: int = 50,
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
"""Estimate density and temperature profiles using profiles from Pablo Rodriguez-Fernandez.
Args:
average_electron_density: [1e19 m^-3] :term:`glossary link<average_electron_density>`
average_electron_temp: [keV] :term:`glossary link<average_electron_temp>`
average_ion_temp: [keV] :term:`glossary link<average_ion_temp>`
electron_density_peaking: [~] :term:`glossary link<electron_density_peaking>`
ion_density_peaking: [~] :term:`glossary link<ion_density_peaking>`
temperature_peaking: [~] :term:`glossary link<temperature_peaking>`
dilution: dilution of main ions [~]
normalized_inverse_temp_scale_length: [~] :term:`glossary link<normalized_inverse_temp_scale_length>`
npoints: number of points to return in profile
Returns:
:term:`rho` [~], :term:`electron_density_profile` [1e19 m^-3], fuel_ion_density_profile [1e19 m^-3], :term:`electron_temp_profile` [keV], :term:`ion_temp_profile` [keV]
"""
rho: np.ndarray = np.linspace(0.0, 1.0, num=npoints, endpoint=False)
rho, electron_temp_profile, electron_density_profile = evaluate_density_and_temperature_profile_fits(
average_electron_temp,
average_electron_density,
temperature_peaking,
electron_density_peaking,
aLT=normalized_inverse_temp_scale_length,
rho=rho,
dataset="PRF",
)
rho, ion_temp_profile, fuel_ion_density_profile = evaluate_density_and_temperature_profile_fits(
average_ion_temp,
average_electron_density * dilution,
temperature_peaking,
ion_density_peaking,
aLT=normalized_inverse_temp_scale_length,
rho=rho,
dataset="PRF",
)
return rho, electron_density_profile, fuel_ion_density_profile, electron_temp_profile, ion_temp_profile