From 0d7fdc43ef96a2e5817d81613380b1282f09383a Mon Sep 17 00:00:00 2001 From: Timothy Nunn Date: Thu, 14 Nov 2024 11:45:39 +0000 Subject: [PATCH] Convert final_module to Python --- CMakeLists.txt | 1 - process/final.py | 85 +++++++++++++++++++--- source/fortran/final_module.f90 | 125 -------------------------------- 3 files changed, 74 insertions(+), 137 deletions(-) delete mode 100644 source/fortran/final_module.f90 diff --git a/CMakeLists.txt b/CMakeLists.txt index 1af3fc1538..470b63d015 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,7 +79,6 @@ LIST(APPEND PROCESS_SRCS pfcoil.f90 reinke_module.f90 sctfcoil.f90 - final_module.f90 cost_variables.f90 divertor_variables.f90 fwbs_variables.f90 diff --git a/process/final.py b/process/final.py index c4df6ae9ca..df764b9770 100644 --- a/process/final.py +++ b/process/final.py @@ -1,12 +1,19 @@ """Final output at the end of a scan.""" -from process import fortran as ft -from process.fortran import final_module as fm +from tabulate import tabulate + +from process.fortran import ( + process_output as po, + constants, + function_evaluator, + numerics, + constraints, +) from process import output as op -from process.fortran import process_output as po +from process.utilities.f2py_string_patch import f2py_compatible_to_string -def finalise(models, ifail, non_idempotent_msg: None | str = None): +def finalise(models, ifail: int, non_idempotent_msg: None | str = None): """Routine to print out the final point in the scan. Writes to OUT.DAT and MFILE.DAT. @@ -18,18 +25,74 @@ def finalise(models, ifail, non_idempotent_msg: None | str = None): :param non_idempotent_msg: warning about non-idempotent variables, defaults to None :type non_idempotent_msg: None | str, optional """ - fm.final_header(ifail) + if ifail == 1: + po.oheadr(constants.nout, "Final Feasible Point") + else: + po.oheadr(constants.nout, "Final UNFEASIBLE Point") # Output relevant to no optimisation - if ft.numerics.ioptimz == -2: - fm.no_optimisation() + if numerics.ioptimz == -2: + output_once_through() # Print non-idempotence warning to OUT.DAT only if non_idempotent_msg: - po.oheadr(ft.constants.nout, "NON-IDEMPOTENT VARIABLES") - po.ocmmnt(ft.constants.nout, non_idempotent_msg) + po.oheadr(constants.nout, "NON-IDEMPOTENT VARIABLES") + po.ocmmnt(constants.nout, non_idempotent_msg) # Write output to OUT.DAT and MFILE.DAT - op.write(models, ft.constants.nout) + op.write(models, constants.nout) + + +def output_once_through(): + """Write output for a once-through run of PROCESS""" + po.oheadr(constants.nout, "Numerics") + po.ocmmnt(constants.nout, "PROCESS has performed a run witout optimisation.") + po.oblnkl(constants.nout) + + # Evaluate objective function + norm_objf = function_evaluator.funfom() + po.ovarre( + constants.mfile, "Normalised objective function", "(norm_objf)", norm_objf + ) + + # Print the residuals of the constraint equations + + residual_error, value, residual, symbols, units = constraints.constraint_eqns( + numerics.neqns + numerics.nineqns, -1 + ) + + labels = [ + f2py_compatible_to_string(i) + for i in numerics.lablcc[numerics.icc[: numerics.neqns + numerics.nineqns]] + ] + units = [f2py_compatible_to_string(i) for i in units] + physical_constraint = [f"{c} {u}" for c, u in zip(value.tolist(), units)] + physical_residual = [f"{c} {u}" for c, u in zip(residual.tolist(), units)] + + table_data = { + "Constraint Name": labels, + "Constraint Type": symbols.tolist(), + "Physical constraint": physical_constraint, + "Constraint residual": physical_residual, + "Normalised residual": residual_error.tolist(), + } + + po.write(constants.nout, tabulate(table_data, headers="keys")) + + for i in range(numerics.neqns): + constraint_id = numerics.icc[i] + po.ovarre( + constants.mfile, + f"{labels[i]} normalised residue", + f"(eq_con{constraint_id:03d})", + residual_error[i], + ) - fm.final_output() + for i in range(numerics.nineqns): + constraint_id = numerics.icc[numerics.neqns + i] + po.ovarre( + constants.mfile, + f"{labels[numerics.neqns + i]}", + f"(ineq_con{constraint_id:03d})", + residual_error[numerics.neqns + i], + ) diff --git a/source/fortran/final_module.f90 b/source/fortran/final_module.f90 deleted file mode 100644 index 06ae0c2680..0000000000 --- a/source/fortran/final_module.f90 +++ /dev/null @@ -1,125 +0,0 @@ -module final_module -#ifndef dp - use, intrinsic :: iso_fortran_env, only: dp=>real64 -#endif - implicit none - - contains - - subroutine final_header(ifail) - use process_output, only: oheadr - use constants, only: nout - implicit none - - integer, intent(in) :: ifail - - if (ifail == 1) then - call oheadr(nout,'Final Feasible Point') - else - call oheadr(nout,'Final UNFEASIBLE Point') - end if - end subroutine final_header - - subroutine no_optimisation() - use constants, only: mfile, nout - use numerics, only: neqns, nineqns, ipeqns, icc, lablcc, rcm, norm_objf - use process_output, only: int_to_string3, ovarre, ocmmnt, oblnkl, osubhd, & - oheadr - use constraints, only: constraint_eqns - use function_evaluator, only: funfom - - implicit none - - integer :: inn - real(dp), dimension(ipeqns) :: con1, con2, err - character(len=1), dimension(ipeqns) :: sym - character(len=10), dimension(ipeqns) :: lab - - - call oheadr(nout,'Numerics') - call ocmmnt(nout,'PROCESS has performed a run witout optimisation.') - call oblnkl(nout) - - ! Evaluate objective function - call funfom(norm_objf) - call ovarre(mfile,'Normalised objective function','(norm_objf)',norm_objf) - - ! Print the residuals of the constraint equations - call constraint_eqns(neqns+nineqns,-1,con1,con2,err,sym,lab) - write(nout,120) - 120 format(t48,'physical',t73,'constraint',t100,'normalised') - write(nout,130) - 130 format(t47,'constraint',t74,'residue',t101,'residue') - call oblnkl(nout) - do inn = 1,neqns - write(nout,140) inn,lablcc(icc(inn)),sym(inn),con2(inn), & - lab(inn),err(inn),lab(inn),con1(inn) - call ovarre(mfile,lablcc(icc(inn))//' normalised residue', & - '(eq_con'//int_to_string3(icc(inn))//')',con1(inn)) - end do - - 140 format(t2,i4,t8,a33,t46,a1,t47,1pe12.4,t60,a10,t71,1pe12.4,t84,a10,t98,1pe12.4) - - if (nineqns > 0) then - call osubhd(nout, & - 'The following inequality constraint residues should be greater than or approximately equal to zero :') - - do inn = neqns+1,neqns+nineqns - write(nout,140) inn,lablcc(icc(inn)),sym(inn),con2(inn), & - lab(inn), err(inn), lab(inn) - call ovarre(mfile,lablcc(icc(inn)),'(ineq_con'//int_to_string3(icc(inn))//')',con1(inn)) - end do - end if - end subroutine no_optimisation - - subroutine final_output() - use constants, only: iotty, mfile - use numerics, only: nfev1, ncalls, xcm, ioptimz, nviter, & - nvar - use define_iteration_variables, only: loadxc - implicit none - - if (nfev1 == 0) then ! no HYBRD call - !if (nviter == 1) then - ! write(iotty,10) nviter,ncalls - !else - ! write(iotty,20) nviter,ncalls - !end if - else if (nviter == 0) then ! no VMCON call - if (nfev1 == 1) then - write(iotty,30) nfev1,ncalls - else - write(iotty,40) nfev1,ncalls - end if - else if (nfev1 == 1) then ! (unlikely that nviter is also 1...) - write(iotty,50) nfev1,nviter,ncalls - else if (nviter == 1) then ! (unlikely that nfev1 is also 1...) - write(iotty,60) nfev1,nviter,ncalls - else - write(iotty,70) nfev1,nviter,ncalls - end if - - 30 format( & - t2,'The HYBRD point required ',i5,' iteration',/, & - t2,'There were ',i6,' function calls') - 40 format( & - t2,'The HYBRD point required ',i5,' iterations',/, & - t2,'There were ',i6,' function calls') - 50 format( & - t2,'The HYBRD point required ',i5,' iteration',/, & - t2,'The optimisation required ',i5,' iterations',/, & - t2,'There were ',i6,' function calls') - 60 format( & - t2,'The HYBRD point required ',i5,' iterations',/, & - t2,'The optimisation required ',i5,' iteration',/, & - t2,'There were ',i6,' function calls') - 70 format( & - t2,'The HYBRD point required ',i5,' iterations',/, & - t2,'The optimisation required ',i5,' iterations',/, & - t2,'There were ',i6,' function calls') - - ! Required to ensure mfile fully written before any model evaluation - ! idempotence checks - flush(mfile) - end subroutine final_output -end module final_module