Skip to content

Commit 9ef189e

Browse files
authored
TokaMaker: Add subroutines for computing local shear and NT H-mode access proxy (OpenFUSIONToolkit#338)
- Add function to compute simple NT H-mode access proxy from [Nelson et al. (2022)] - Add support for computing local shear (`TokaMaker_equilibrium.calc_local_shear()`) - Add support for interpolating general nodal fields in TokaMaker through `TokaMaker.get_field_eval()` - Add support for evaluating multiple points at once in interpolation objects returned by `TokaMaker.get_field_eval()` * Add start of generic FE interpolation support (just surface Lagrange for now) - Add the ability to retrieve fields on node points using FE projection (`TokaMaker_equilibrium.get_nodal_field()`) - Add support for uniform resampling in `TokaMaker.trace_surf()` - Fix bug in Ip and centroid calculations in `gs_comp_globals` when anisotropic pressure is used * This change impacts `TokaMaker_equilibrium.get_stats()` and all downstream consumers (eg. `print_info()`)
1 parent 5da9985 commit 9ef189e

12 files changed

Lines changed: 942 additions & 140 deletions

File tree

src/base/oft_local.F90

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
!--------------------------------------------------------------------------------
1616
MODULE oft_local
1717
USE, INTRINSIC :: iso_c_binding, only: c_int, c_ptr, c_long
18+
USE, INTRINSIC :: ieee_arithmetic, only: ieee_value, ieee_quiet_nan
1819
#ifdef __INTEL_COMPILER
1920
USE ifport ! Intel fortran portability library
2021
#endif

src/examples/TokaMaker/DIIID/DIIID_baseline_ex.ipynb

Lines changed: 137 additions & 73 deletions
Large diffs are not rendered by default.

src/fem/blag_operators.F90

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ MODULE oft_blag_operators
7676
!------------------------------------------------------------------------------
7777
type, extends(bfem_interp) :: oft_lag_bvrinterp
7878
class(oft_vector), pointer :: u => NULL() !< Field for interpolation
79+
class(oft_vector), pointer :: ux => NULL() !< Field for interpolation
80+
class(oft_vector), pointer :: uy => NULL() !< Field for interpolation
81+
class(oft_vector), pointer :: uz => NULL() !< Field for interpolation
7982
real(r8), pointer, dimension(:,:) :: vals => NULL() !< Local values
8083
class(oft_scalar_bfem), pointer :: lag_rep => NULL() !< Lagrange FE representation
8184
contains
@@ -87,6 +90,15 @@ MODULE oft_blag_operators
8790
procedure :: delete => lag_bvrinterp_delete
8891
end type oft_lag_bvrinterp
8992
!------------------------------------------------------------------------------
93+
!> Interpolate \f$ \nabla \f$ of a Lagrange field
94+
!------------------------------------------------------------------------------
95+
type, extends(oft_lag_bvrinterp) :: oft_lag_bvcinterp
96+
logical :: cylindrical = .FALSE. !< Curl in cylindrical coordinates?
97+
contains
98+
!> Reconstruct field
99+
procedure :: interp => lag_bvcinterp
100+
end type oft_lag_bvcinterp
101+
!------------------------------------------------------------------------------
90102
!> Needs docs
91103
!------------------------------------------------------------------------------
92104
type, extends(oft_solver_bc) :: oft_blag_zerob
@@ -256,12 +268,23 @@ subroutine lag_bvrinterp_setup(self,lag_rep)
256268
self%mesh=>self%lag_rep%mesh
257269
!---Get local slice
258270
IF(.NOT.ASSOCIATED(self%vals))ALLOCATE(self%vals(3,self%lag_rep%ne))
259-
vtmp=>self%vals(1,:)
260-
CALL self%u%get_local(vtmp,1)
261-
vtmp=>self%vals(2,:)
262-
CALL self%u%get_local(vtmp,2)
263-
vtmp=>self%vals(3,:)
264-
CALL self%u%get_local(vtmp,3)
271+
IF(ASSOCIATED(self%u))THEN
272+
vtmp=>self%vals(1,:)
273+
CALL self%u%get_local(vtmp,1)
274+
vtmp=>self%vals(2,:)
275+
CALL self%u%get_local(vtmp,2)
276+
vtmp=>self%vals(3,:)
277+
CALL self%u%get_local(vtmp,3)
278+
ELSE IF(ASSOCIATED(self%ux))THEN
279+
vtmp=>self%vals(1,:)
280+
CALL self%ux%get_local(vtmp)
281+
vtmp=>self%vals(2,:)
282+
CALL self%uy%get_local(vtmp)
283+
vtmp=>self%vals(3,:)
284+
CALL self%uz%get_local(vtmp)
285+
ELSE
286+
CALL oft_abort("No vector associated","lag_bvrinterp_setup",__FILE__)
287+
END IF
265288
end subroutine lag_bvrinterp_setup
266289
!------------------------------------------------------------------------------
267290
!> Destroy temporary internal storage
@@ -301,6 +324,49 @@ subroutine lag_bvrinterp(self,cell,f,gop,val)
301324
DEBUG_STACK_POP
302325
end subroutine lag_bvrinterp
303326
!------------------------------------------------------------------------------
327+
!> Reconstruct the curl of a boundary Lagrange vector field
328+
!------------------------------------------------------------------------------
329+
subroutine lag_bvcinterp(self,cell,f,gop,val)
330+
class(oft_lag_bvcinterp), intent(inout) :: self
331+
integer(i4), intent(in) :: cell !< Cell for interpolation
332+
real(r8), intent(in) :: f(:) !< Position in cell in logical coord [3]
333+
real(r8), intent(in) :: gop(3,3) !< Logical gradient vectors at f [3,3]
334+
real(r8), intent(out) :: val(:) !< Reconstructed field at f [3]
335+
integer(i4), allocatable :: j(:)
336+
integer(i4) :: jc
337+
real(r8) :: rop(3),fop,pt(3)
338+
DEBUG_STACK_PUSH
339+
!---
340+
IF(.NOT.ASSOCIATED(self%vals))CALL oft_abort('Setup has not been called!', &
341+
'lag_bvcinterp',__FILE__)
342+
!---Get dofs
343+
allocate(j(self%lag_rep%nce))
344+
call self%lag_rep%ncdofs(cell,j) ! get DOFs
345+
!---Reconstruct field
346+
IF(self%cylindrical)THEN
347+
val=0.d0
348+
pt=self%mesh%log2phys(cell,f)
349+
do jc=1,self%lag_rep%nce
350+
call oft_blag_eval(self%lag_rep,cell,jc,f,fop)
351+
call oft_blag_geval(self%lag_rep,cell,jc,f,rop,gop)
352+
rop(3) = rop(2); rop(2) = 0.d0 ! Shuffle 2D gradient to 3D cylindrical
353+
val(1) = val(1) + (self%vals(3,j(jc))*rop(2)/pt(1) - self%vals(2,j(jc))*rop(3))
354+
val(2) = val(2) + (self%vals(1,j(jc))*rop(3) - self%vals(3,j(jc))*rop(1))
355+
val(3) = val(3) + (pt(1)*self%vals(2,j(jc))*rop(1) + self%vals(2,j(jc))*fop - self%vals(1,j(jc))*rop(2))/pt(1)
356+
end do
357+
ELSE
358+
val=0.d0
359+
do jc=1,self%lag_rep%nce
360+
call oft_blag_geval(self%lag_rep,cell,jc,f,rop,gop)
361+
val(1) = val(1) + (self%vals(3,j(jc))*rop(2) - self%vals(2,j(jc))*rop(3))
362+
val(2) = val(2) + (self%vals(1,j(jc))*rop(3) - self%vals(3,j(jc))*rop(1))
363+
val(3) = val(3) + (self%vals(2,j(jc))*rop(1) - self%vals(1,j(jc))*rop(2))
364+
end do
365+
END IF
366+
deallocate(j)
367+
DEBUG_STACK_POP
368+
end subroutine lag_bvcinterp
369+
!------------------------------------------------------------------------------
304370
!> Zero a surface Lagrange scalar field at all boundary nodes
305371
!------------------------------------------------------------------------------
306372
subroutine zerob_apply(self,a)

src/physics/grad_shaf.F90

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ MODULE oft_gs
237237
CLASS(oft_matrix), POINTER :: mop => NULL() !< Lagrange FE mass matrix
238238
CLASS(oft_matrix), POINTER :: mop_axis => NULL() !< Lagrange FE mass matrix with Dirichlet BCs on axis
239239
CLASS(oft_bmesh), POINTER :: mesh => NULL() !< Mesh
240-
CLASS(oft_scalar_bfem), POINTER :: fe_rep => NULL() !< Lagrange FE representation
240+
TYPE(oft_scalar_bfem), POINTER :: fe_rep => NULL() !< Lagrange FE representation
241241
TYPE(oft_ml_fem_type), POINTER :: ML_fe_rep => NULL() !< Multi-level Lagrange FE representation (only top level used)
242242
TYPE(oft_blag_zerob), POINTER :: zerob_bc => NULL() !< BC object for zeroing boundary nodes
243243
TYPE(oft_blag_zerogrnd), POINTER :: zerogrnd_bc => NULL() !< BC object for zeroing grounding node(s)
@@ -4720,7 +4720,7 @@ subroutine gs_prof_interp_apply(self,cell,f,gop,val)
47204720
ELSE
47214721
val(1)=self%equil%psiscale*self%equil%I%f_offset
47224722
END IF
4723-
CASE(3)
4723+
CASE(3) ! Isotropic pressure or perpendicular component for anisotropic pressure
47244724
CALL self%psi_eval%interp(cell,f,gop,psitmp)
47254725
IF(in_plasma.AND.(psitmp(1)>self%equil%plasma_bounds(1)))THEN
47264726
! Handle anisotropic pressure
@@ -4741,6 +4741,19 @@ subroutine gs_prof_interp_apply(self,cell,f,gop,val)
47414741
ELSE
47424742
val(1)=0.d0
47434743
END IF
4744+
CASE(5) ! Isotropic pressure or parallel component for anisotropic pressure
4745+
CALL self%psi_eval%interp(cell,f,gop,psitmp)
4746+
IF(in_plasma.AND.(psitmp(1)>self%equil%plasma_bounds(1)))THEN
4747+
! Handle anisotropic pressure
4748+
IF(ASSOCIATED(self%equil%P_ani))THEN
4749+
CALL self%equil%P_ani%interp(cell,f,gop,pani)
4750+
val(1) = (self%equil%psiscale**2)*self%equil%p_scale*self%equil%P%F(psitmp(1))*pani(1)/mu0
4751+
ELSE
4752+
val(1) = (self%equil%psiscale**2)*self%equil%p_scale*self%equil%P%F(psitmp(1))/mu0
4753+
END IF
4754+
ELSE
4755+
val(1)=0.d0
4756+
END IF
47444757
CASE DEFAULT
47454758
CALL oft_abort('Unknown field mode','gs_prof_interp_apply',__FILE__)
47464759
END SELECT

src/physics/grad_shaf_util.F90

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ MODULE oft_gs_util
2727
USE tracing_2d, ONLY: active_tracer, tracinginv_fs, set_tracer
2828
USE mhd_utils, ONLY: mu0
2929
USE oft_gs, ONLY: gs_factory, flux_func, gs_dflux, gs_itor_nl, gs_test_bounds, gs_b_interp, &
30-
gsinv_interp, gs_psi2r, gs_psi2pt, gs_epsilon, gs_update_bounds
30+
gsinv_interp, gs_psi2r, gs_psi2pt, gs_epsilon, gs_update_bounds, gs_bcrosskappa
3131
USE oft_gs_profiles
3232
USE grad_shaf_prof_phys, ONLY: create_jphi_ff, jphi_flux_func
3333
IMPLICIT NONE
@@ -197,10 +197,10 @@ subroutine gs_comp_globals(self,itor,centroid,vol,pvol,dflux,tflux,bp_vol)
197197
real(8), intent(out) :: dflux !< Diamagnetic flux
198198
real(8), intent(out) :: tflux !< Contained toroidal flux
199199
real(8), intent(out) :: bp_vol !< \f$ \int B_p^2 dV \f$
200-
type(oft_lag_brinterp) :: psi_eval
200+
type(oft_lag_brinterp) :: psi_eval,bcross_kappa_fun
201201
type(oft_lag_bginterp) :: psi_geval
202202
real(8) :: itor_loc,goptmp(3,3),v,psitmp(1),gpsitmp(3)
203-
real(8) :: pt(3),curr_cent(2),Btor,Bpol(2)
203+
real(8) :: pt(3),curr_cent(2),Btor,Bpol(2),pani(2),bcross_kappa(1)
204204
integer(4) :: i,m
205205
class(oft_bmesh), pointer :: smesh
206206
type(gs_factory), pointer :: device
@@ -210,6 +210,13 @@ subroutine gs_comp_globals(self,itor,centroid,vol,pvol,dflux,tflux,bp_vol)
210210
psi_eval%u=>self%psi
211211
CALL psi_eval%setup(device%fe_rep)
212212
CALL psi_geval%shared_setup(psi_eval)
213+
IF(ASSOCIATED(self%P_ani))THEN
214+
CALL self%psi%new(bcross_kappa_fun%u)
215+
CALL gs_bcrosskappa(self,bcross_kappa_fun%u)
216+
CALL bcross_kappa_fun%setup(device%fe_rep)
217+
ELSE
218+
NULLIFY(bcross_kappa_fun%u)
219+
END IF
213220
!---
214221
itor = 0.d0
215222
centroid = 0.d0
@@ -218,7 +225,7 @@ subroutine gs_comp_globals(self,itor,centroid,vol,pvol,dflux,tflux,bp_vol)
218225
dflux = 0.d0
219226
tflux = 0.d0
220227
bp_vol = 0.d0
221-
!$omp parallel do private(m,goptmp,v,psitmp,gpsitmp,pt,itor_loc,Btor,Bpol) &
228+
!$omp parallel do private(m,goptmp,v,psitmp,gpsitmp,pt,itor_loc,Btor,Bpol,bcross_kappa,pani) &
222229
!$omp reduction(+:itor) reduction(+:centroid) reduction(+:pvol) reduction(+:vol) reduction(+:dflux) &
223230
!$omp reduction(+:tflux) reduction(+:bp_vol)
224231
do i=1,smesh%nc
@@ -231,11 +238,17 @@ subroutine gs_comp_globals(self,itor,centroid,vol,pvol,dflux,tflux,bp_vol)
231238
!---Compute Magnetic Field
232239
IF(gs_test_bounds(self,pt))THEN
233240
IF(self%mode==0)THEN
234-
itor_loc = (self%p_scale*pt(1)*self%P%Fp(psitmp(1)) &
235-
+ self%I%Fp(psitmp(1))*((self%ffp_scale**2)*self%I%f(psitmp(1))+self%ffp_scale*self%I%f_offset)/(pt(1)+gs_epsilon))
241+
itor_loc = self%I%Fp(psitmp(1))*((self%ffp_scale**2)*self%I%f(psitmp(1))+self%ffp_scale*self%I%f_offset)/(pt(1)+gs_epsilon)
236242
ELSE
237-
itor_loc = (self%p_scale*pt(1)*self%P%Fp(psitmp(1)) &
238-
+ .5d0*self%ffp_scale*self%I%Fp(psitmp(1))/(pt(1)+gs_epsilon))
243+
itor_loc = 0.5d0*self%ffp_scale*self%I%Fp(psitmp(1))/(pt(1)+gs_epsilon)
244+
END IF
245+
! Handle anisotropic pressure
246+
IF(ASSOCIATED(self%P_ani))THEN
247+
CALL self%P_ani%interp(i,device%fe_rep%quad%pts(:,m),goptmp,pani)
248+
CALL bcross_kappa_fun%interp(i,device%fe_rep%quad%pts(:,m),goptmp,bcross_kappa)
249+
itor_loc = itor_loc + self%p_scale*pt(1)*(self%P%fp(psitmp(1))*pani(2)+self%P%f(psitmp(1))*(pani(1)-pani(2))*bcross_kappa(1))
250+
ELSE
251+
itor_loc = itor_loc + self%p_scale*pt(1)*self%P%Fp(psitmp(1))
239252
END IF
240253
itor = itor + itor_loc*v*device%fe_rep%quad%wts(m)
241254
centroid = centroid + itor_loc*pt(1:2)*v*device%fe_rep%quad%wts(m)
@@ -271,6 +284,11 @@ subroutine gs_comp_globals(self,itor,centroid,vol,pvol,dflux,tflux,bp_vol)
271284
bp_vol=bp_vol*self%psiscale*self%psiscale
272285
dflux=dflux*self%psiscale
273286
tflux=tflux*self%psiscale
287+
IF(ASSOCIATED(bcross_kappa_fun%u))THEN
288+
CALL bcross_kappa_fun%u%delete
289+
DEALLOCATE(bcross_kappa_fun%u)
290+
CALL bcross_kappa_fun%delete
291+
END IF
274292
CALL psi_eval%delete
275293
CALL psi_geval%delete
276294
end subroutine gs_comp_globals

0 commit comments

Comments
 (0)