@@ -35,6 +35,8 @@ typedef struct {
3535 int nx , ny , nz ;
3636 float * depth_values ; /* RC[nz] */
3737 float * hfac ; /* hFacC[nz*ny*nx], NULL if unavailable */
38+ float * angle_cs ; /* AngleCS[ny*nx], NULL if unavailable */
39+ float * angle_sn ; /* AngleSN[ny*nx], NULL if unavailable */
3840 float missing_value ;
3941} MitgcmStore ;
4042
@@ -47,6 +49,8 @@ typedef struct {
4749 int nx , ny , nz ;
4850 float missing_value ;
4951 int has_missing ;
52+ int velocity_component ; /* 0=none, 1=U-component, 2=V-component */
53+ USVar * paired_velocity ; /* paired U/V variable, NULL if unpaired */
5054} MitgcmVarData ;
5155
5256/* ---- Byte swapping ---- */
@@ -435,9 +439,38 @@ USFile *mitgcm_open(const char *path) {
435439 }
436440 }
437441
442+ /* Load AngleCS/AngleSN (or CS/SN) for velocity rotation */
443+ {
444+ size_t grid_size = (size_t )ny * nx ;
445+ char cs_path [MAX_NAME_LEN ], sn_path [MAX_NAME_LEN ];
446+
447+ /* Try AngleCS.data / AngleSN.data first */
448+ snprintf (cs_path , sizeof (cs_path ), "%s/AngleCS.data" , directory );
449+ snprintf (sn_path , sizeof (sn_path ), "%s/AngleSN.data" , directory );
450+ if (!file_exists (cs_path ) || !file_exists (sn_path )) {
451+ /* Fall back to CS.data / SN.data */
452+ snprintf (cs_path , sizeof (cs_path ), "%s/CS.data" , directory );
453+ snprintf (sn_path , sizeof (sn_path ), "%s/SN.data" , directory );
454+ }
455+
456+ if (file_exists (cs_path ) && file_exists (sn_path )) {
457+ store -> angle_cs = read_binary_floats (cs_path , 0 , grid_size );
458+ store -> angle_sn = read_binary_floats (sn_path , 0 , grid_size );
459+ if (store -> angle_cs && store -> angle_sn ) {
460+ printf ("MITgcm: loaded AngleCS/AngleSN for velocity rotation\n" );
461+ } else {
462+ /* Both must succeed */
463+ free (store -> angle_cs ); store -> angle_cs = NULL ;
464+ free (store -> angle_sn ); store -> angle_sn = NULL ;
465+ }
466+ }
467+ }
468+
438469 /* Create USFile */
439470 USFile * f = calloc (1 , sizeof (USFile ));
440- if (!f ) { free (store -> depth_values ); free (store -> hfac ); free (store ); return NULL ; }
471+ if (!f ) { free (store -> depth_values ); free (store -> hfac );
472+ free (store -> angle_cs ); free (store -> angle_sn );
473+ free (store ); return NULL ; }
441474
442475 f -> file_type = FILE_TYPE_MITGCM ;
443476 strncpy (f -> filename , directory , MAX_NAME_LEN - 1 );
@@ -723,6 +756,34 @@ USVar *mitgcm_scan_variables(USFile *f, USMesh *m) {
723756 free (iterations );
724757 }
725758
759+ /* Detect velocity components and pair U/V variables */
760+ if (store -> angle_cs ) {
761+ /* Mark velocity components by name */
762+ for (USVar * v = head ; v ; v = v -> next ) {
763+ MitgcmVarData * d = (MitgcmVarData * )v -> mitgcm_data ;
764+ if (v -> name [0 ] == 'U' ) d -> velocity_component = 1 ;
765+ else if (v -> name [0 ] == 'V' ) d -> velocity_component = 2 ;
766+ }
767+
768+ /* Pair U/V variables: same prefix, name differs only in first char */
769+ for (USVar * u = head ; u ; u = u -> next ) {
770+ MitgcmVarData * ud = (MitgcmVarData * )u -> mitgcm_data ;
771+ if (ud -> velocity_component != 1 || ud -> paired_velocity ) continue ;
772+
773+ for (USVar * v = head ; v ; v = v -> next ) {
774+ MitgcmVarData * vvd = (MitgcmVarData * )v -> mitgcm_data ;
775+ if (vvd -> velocity_component != 2 || vvd -> paired_velocity ) continue ;
776+ if (strcmp (ud -> prefix , vvd -> prefix ) != 0 ) continue ;
777+ if (strcmp (u -> name + 1 , v -> name + 1 ) != 0 ) continue ;
778+
779+ ud -> paired_velocity = v ;
780+ vvd -> paired_velocity = u ;
781+ printf ("MITgcm: paired %s / %s for rotation\n" , u -> name , v -> name );
782+ break ;
783+ }
784+ }
785+ }
786+
726787 f -> vars = head ;
727788 f -> n_vars = n_vars ;
728789 printf ("MITgcm: found %d variables from %d diagnostic groups\n" , n_vars , n_prefixes );
@@ -782,6 +843,46 @@ int mitgcm_read_slice(USVar *var, size_t time_idx, size_t depth_idx, float *data
782843 swap_endian_float (data , slab_size );
783844 }
784845
846+ /* Apply CS/SN velocity rotation */
847+ if (store -> angle_cs && vd -> velocity_component != 0 && vd -> paired_velocity ) {
848+ MitgcmVarData * pvd = (MitgcmVarData * )vd -> paired_velocity -> mitgcm_data ;
849+
850+ /* Compute offset for the paired component in the same data file */
851+ size_t p_field_size = pvd -> is_3d ? (size_t )pvd -> nz * slab_size : slab_size ;
852+ size_t p_offset = (size_t )pvd -> field_index * p_field_size ;
853+ if (pvd -> is_3d ) p_offset += depth_idx * slab_size ;
854+ p_offset *= sizeof (float );
855+
856+ float * other = malloc (slab_size * sizeof (float ));
857+ if (other ) {
858+ FILE * fp2 = fopen (data_path , "rb" );
859+ if (fp2 ) {
860+ fseek (fp2 , (long )p_offset , SEEK_SET );
861+ size_t pr = fread (other , sizeof (float ), slab_size , fp2 );
862+ fclose (fp2 );
863+
864+ if (pr == slab_size ) {
865+ if (is_little_endian ()) swap_endian_float (other , slab_size );
866+
867+ if (vd -> velocity_component == 1 ) {
868+ /* This is U, other is V: u_east = CS*U - SN*V */
869+ for (size_t i = 0 ; i < slab_size ; i ++ ) {
870+ float u = data [i ], v = other [i ];
871+ data [i ] = store -> angle_cs [i ] * u - store -> angle_sn [i ] * v ;
872+ }
873+ } else {
874+ /* This is V, other is U: v_north = SN*U + CS*V */
875+ for (size_t i = 0 ; i < slab_size ; i ++ ) {
876+ float u = other [i ], v = data [i ];
877+ data [i ] = store -> angle_sn [i ] * u + store -> angle_cs [i ] * v ;
878+ }
879+ }
880+ }
881+ }
882+ free (other );
883+ }
884+ }
885+
785886 /* Apply hFacC land masking */
786887 if (store -> hfac && vd -> is_3d ) {
787888 float * mask = store -> hfac + depth_idx * slab_size ;
@@ -976,6 +1077,32 @@ int mitgcm_read_timeseries(USVar *var, size_t node_idx, size_t depth_idx,
9761077 if (fread (& val , sizeof (float ), 1 , fp ) == 1 ) {
9771078 if (is_little_endian ()) swap_endian_float (& val , 1 );
9781079
1080+ /* Apply CS/SN velocity rotation */
1081+ if (store -> angle_cs && vd -> velocity_component != 0 && vd -> paired_velocity ) {
1082+ MitgcmVarData * pvd = (MitgcmVarData * )vd -> paired_velocity -> mitgcm_data ;
1083+ size_t p_field_size = pvd -> is_3d ? (size_t )pvd -> nz * slab_size : slab_size ;
1084+ size_t p_off = (size_t )pvd -> field_index * p_field_size ;
1085+ if (pvd -> is_3d ) p_off += depth_idx * slab_size ;
1086+ p_off += node_idx ;
1087+ p_off *= sizeof (float );
1088+
1089+ float other_val ;
1090+ fseek (fp , (long )p_off , SEEK_SET );
1091+ if (fread (& other_val , sizeof (float ), 1 , fp ) == 1 ) {
1092+ if (is_little_endian ()) swap_endian_float (& other_val , 1 );
1093+ if (vd -> velocity_component == 1 ) {
1094+ /* U: u_east = CS*U - SN*V */
1095+ val = store -> angle_cs [node_idx ] * val
1096+ - store -> angle_sn [node_idx ] * other_val ;
1097+ } else {
1098+ /* V: v_north = SN*U + CS*V */
1099+ float u = other_val , v = val ;
1100+ val = store -> angle_sn [node_idx ] * u
1101+ + store -> angle_cs [node_idx ] * v ;
1102+ }
1103+ }
1104+ }
1105+
9791106 /* Check hFacC mask */
9801107 int masked = 0 ;
9811108 if (store -> hfac && vd -> is_3d ) {
@@ -1014,6 +1141,8 @@ void mitgcm_close(USFile *file) {
10141141 MitgcmStore * store = (MitgcmStore * )file -> mitgcm_data ;
10151142 free (store -> depth_values );
10161143 free (store -> hfac );
1144+ free (store -> angle_cs );
1145+ free (store -> angle_sn );
10171146 free (store );
10181147 }
10191148
0 commit comments