diff --git a/ChangeLog b/ChangeLog
index 3c80e270fd6a9f5ce038c6c5bf7dc5045d2a47b9..093a8abe46e093501102c655d40c696409a862ef 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,14 @@
 
 	* Version 2.5.1 released
 
+2025-02-28  Uwe Schulzweida
+
+	* taxis: added support for CDI_KEY_DATATYPE
+
+2025-02-12  Uwe Schulzweida
+
+	* GRIB_API: Handle LLAM as LCC
+
 2025-02-06  Uwe Schulzweida
 
 	* Added obsolete functions vlistNgrids() and vlistNzaxis() for ParaView vtkCDIReader
diff --git a/app/printinfo.c b/app/printinfo.c
index ccbeea0049bdb477e3734ebe858d7251880cf61f..8fd902e0b769628db06bbcf9e152f9938f603bf2 100644
--- a/app/printinfo.c
+++ b/app/printinfo.c
@@ -300,8 +300,8 @@ printGridInfoKernel(int gridID, int index, bool lproj)
   size_t xsize = (size_t) gridInqXsize(gridID);
   size_t ysize = (size_t) gridInqYsize(gridID);
 
-  // int prec     = gridInqDatatype(gridID);
-  // int dig = (prec == CDI_DATATYPE_FLT64) ? 15 : 7;
+  // int datatype;
+  // cdiInqKeyInt(gridID, CDI_GLOBAL, CDI_KEY_DATATYPE, &datatype);
   int dig = 7;
 
   if (!lproj)
@@ -537,8 +537,9 @@ printZaxisInfo(int vlistID)
       int ltype = 0;
       cdiInqKeyInt(zaxisID, CDI_GLOBAL, CDI_KEY_TYPEOFFIRSTFIXEDSURFACE, &ltype);
       int levelsize = zaxisInqSize(zaxisID);
-      // int prec      = zaxisInqDatatype(zaxisID);
-      // int dig = (prec == CDI_DATATYPE_FLT64) ? 15 : 7;
+      // int datatype;
+      // cdiInqKeyInt(gridID, CDI_GLOBAL, CDI_KEY_DATATYPE, &datatype);
+      // int dig = (datatype == CDI_DATATYPE_FLT64) ? 15 : 7;
 
       zaxisName(zaxistype, zaxisname);
       int length = CDI_MAX_NAME;
diff --git a/examples/pio/compareResourcesArray.c b/examples/pio/compareResourcesArray.c
index 0082d60caee0271025039752166cee9fd0e94bd0..4eb771089b26c079bb3cbce5fa842bab1cc4cae1 100644
--- a/examples/pio/compareResourcesArray.c
+++ b/examples/pio/compareResourcesArray.c
@@ -60,7 +60,7 @@ defineGrid()
   cdiDefKeyString(gridID, CDI_XAXIS, CDI_KEY_UNITS, "myXunits");
   cdiDefKeyString(gridID, CDI_YAXIS, CDI_KEY_UNITS, "myYunits");
 
-  gridDefDatatype(gridID, DOUBLE_PRECISION);
+  cdiDefKeyInt(gridID, CDI_GLOBAL, CDI_KEY_DATATYPE, DOUBLE_PRECISION);
   gridDefTrunc(gridID, 1);
 
   cdiDefKeyInt(gridID, CDI_GLOBAL, CDI_KEY_NUMBEROFGRIDUSED, 6);
diff --git a/src/cdi_key.c b/src/cdi_key.c
index b80d936df41b9cf64976cf9639131ab2e8cd4bc4..3f0351a1e366d8eac9054cf0e10e30d82c3c6abe 100644
--- a/src/cdi_key.c
+++ b/src/cdi_key.c
@@ -12,6 +12,7 @@
 
 #include "cdi.h"
 #include "cdi_int.h"
+#include "taxis.h"
 #include "zaxis.h"
 #include "grid.h"
 #include "vlist.h"
@@ -43,6 +44,12 @@ zaxis_get_keysp(zaxis_t *zaxisptr, int varID)
   return (varID == CDI_GLOBAL) ? &zaxisptr->keys : NULL;
 }
 
+static cdi_keys_t *
+taxis_get_keysp(taxis_t *taxisptr, int varID)
+{
+  return (varID == CDI_GLOBAL) ? &taxisptr->keys : NULL;
+}
+
 static cdi_key_t *
 new_key(cdi_keys_t *keysp, int key)
 {
@@ -100,6 +107,7 @@ cdi_get_keysp(int objID, int varID)
   if (reshID == GRID) return grid_get_keysp(grid_to_pointer(objID), varID);
   if (reshID == DIST_GRID) return grid_get_keysp(grid_to_pointer(objID), varID);
   if (reshID == ZAXIS) return zaxis_get_keysp(zaxis_to_pointer(objID), varID);
+  if (reshID == TAXIS) return taxis_get_keysp(taxis_to_pointer(objID), varID);
   if (reshID == VLIST) return vlist_get_keysp(vlist_to_pointer(objID), varID);
 
   return NULL;
diff --git a/src/gribapi.h b/src/gribapi.h
index 87e9a23e30fa062572fc3314a9e18d5a71d8fdf7..784eb940a8e64e75d1f69e54d2b8fa50645ed675 100644
--- a/src/gribapi.h
+++ b/src/gribapi.h
@@ -49,6 +49,7 @@
 #define  GRIB2_GTYPE_LATLON_ROTSTR         3  // Stretched and Rotated Latitude/longitude
 #define  GRIB2_GTYPE_STERE                20  // Polar stereographic projection
 #define  GRIB2_GTYPE_LCC                  30  // Lambert conformal
+#define  GRIB2_GTYPE_LLAM                 33  // Lambert LAM
 #define  GRIB2_GTYPE_GAUSSIAN             40  // Gaussian latitude/longitude
 #define  GRIB2_GTYPE_GAUSSIAN_ROT         41  // Rotated Gaussian latitude/longitude
 #define  GRIB2_GTYPE_GAUSSIAN_STR         42  // Stretched Gaussian latitude/longitude
diff --git a/src/gribapi_utilities.c b/src/gribapi_utilities.c
index 068b7f4bbc062d838540238b83245fad48108df0..ee5b158633b7760dac7ff66825c1b92c778cda80 100644
--- a/src/gribapi_utilities.c
+++ b/src/gribapi_utilities.c
@@ -591,6 +591,7 @@ gribapiGetGridType(grib_handle *gh)
     case GRIB2_GTYPE_GAUSSIAN: return has_ni(gh) ? GRID_GAUSSIAN : GRID_GAUSSIAN_REDUCED;
     case GRIB2_GTYPE_LATLON_ROT: return GRID_PROJECTION;
     case GRIB2_GTYPE_LCC: return CDI_PROJ_LCC;
+    case GRIB2_GTYPE_LLAM: return CDI_PROJ_LCC;  // Handle LLAM as LCC
     case GRIB2_GTYPE_STERE: return CDI_PROJ_STERE;
     case GRIB2_GTYPE_SPECTRAL: return GRID_SPECTRAL;
     case GRIB2_GTYPE_GME: return GRID_GME;
diff --git a/src/pio_server.c b/src/pio_server.c
index bdda03d49c0726350b4daae0ffbfecbd6ceb01b5..a9f4c7c3dd66ef03bcd985ccaae950e450448bbc 100644
--- a/src/pio_server.c
+++ b/src/pio_server.c
@@ -246,8 +246,8 @@ readFuncCall(struct winHeaderEntry *header, size_t streamIdx)
         int position = header->offset;
         int changedTaxisID = taxisUnpack((char *) rxWin[streamIdx].clientBuf[0].mem, (int) rxWin[streamIdx].clientBuf[0].size,
                                          &position, originNamespace, &pioInterComm, 0);
-        taxis_t *oldTaxisPtr = taxisPtr(oldTaxisID);
-        taxis_t *changedTaxisPtr = taxisPtr(changedTaxisID);
+        taxis_t *oldTaxisPtr = taxis_to_pointer(oldTaxisID);
+        taxis_t *changedTaxisPtr = taxis_to_pointer(changedTaxisID);
         ptaxisCopy(oldTaxisPtr, changedTaxisPtr);
         taxisDestroy(changedTaxisID);
         streamDefTimestep(streamID, funcArgs->streamNewTimestep.tsID);
diff --git a/src/stream.c b/src/stream.c
index 34aabfbab941d64afdf4da3f208169b46c69d62f..f3a11f7c81df0494610670f4a9aa27cea5621718 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -430,7 +430,7 @@ cdiInqContents(stream_t *streamptr)
       if (taxisID != CDI_UNDEFID)
         {
           taxis_t *taxisptr1 = &streamptr->tsteps[0].taxis;
-          taxis_t *taxisptr2 = taxisPtr(taxisID);
+          taxis_t *taxisptr2 = taxis_to_pointer(taxisID);
           ptaxisCopy(taxisptr2, taxisptr1);
         }
     }
@@ -1462,7 +1462,7 @@ cdiStreamDefTimestep_(stream_t *streamptr, int tsID)
     }
 
   int taxisID = vlistInqTaxis(vlistID);
-  if (taxisID != CDI_UNDEFID) ptaxisCopy(&streamptr->tsteps[tsID].taxis, taxisPtr(taxisID));
+  if (taxisID != CDI_UNDEFID) ptaxisCopy(&streamptr->tsteps[tsID].taxis, taxis_to_pointer(taxisID));
 
   streamptr->curTsID = tsID;
   streamptr->ntsteps = tsID + 1;
@@ -1564,7 +1564,7 @@ streamInqTimestep(int streamID, int tsID)
       streamptr->tsteps[tsID].curRecID = CDI_UNDEFID;
       int taxisID = vlistInqTaxis(vlistID);
       if (taxisID == -1) Error("Timestep undefined for fileID = %d", streamID);
-      ptaxisCopy(taxisPtr(taxisID), &streamptr->tsteps[tsID].taxis);
+      ptaxisCopy(taxis_to_pointer(taxisID), &streamptr->tsteps[tsID].taxis);
 
       return nrecs;
     }
@@ -1633,7 +1633,7 @@ streamInqTimestep(int streamID, int tsID)
   int taxisID = vlistInqTaxis(vlistID);
   if (taxisID == -1) Error("Timestep undefined for fileID = %d", streamID);
 
-  ptaxisCopy(taxisPtr(taxisID), &streamptr->tsteps[tsID].taxis);
+  ptaxisCopy(taxis_to_pointer(taxisID), &streamptr->tsteps[tsID].taxis);
 
   return nrecs;
 }
@@ -1832,7 +1832,7 @@ cdiStreamSetupVlist_(stream_t *streamptr, int vlistID)
           if (taxisInqType(taxisID) == TAXIS_RELATIVE)
             if (cdiBaseFiletype(streamptr->filetype) == CDI_FILETYPE_NETCDF)
               {
-                const taxis_t *taxisptr = taxisPtr(taxisID);
+                const taxis_t *taxisptr = taxis_to_pointer(taxisID);
                 if (cdiDateTime_isNull(taxisptr->rDateTime))
                   {
                     int vdate = taxisInqVdate(taxisID);
@@ -1841,7 +1841,7 @@ cdiStreamSetupVlist_(stream_t *streamptr, int vlistID)
                   }
               }
 #endif
-          ptaxisCopy(&streamptr->tsteps[0].taxis, taxisPtr(taxisID));
+          ptaxisCopy(&streamptr->tsteps[0].taxis, taxis_to_pointer(taxisID));
         }
 
       switch (cdiBaseFiletype(streamptr->filetype))
diff --git a/src/stream_cdf_time.c b/src/stream_cdf_time.c
index dc594c44d4d14dd8080fc484636b7cdff7f8ecfb..8b5e662a99bdec5b897c928e9daa94477331bb9b 100644
--- a/src/stream_cdf_time.c
+++ b/src/stream_cdf_time.c
@@ -153,7 +153,7 @@ cdfDefTime(stream_t *streamptr)
   if (streamptr->ncmode == 0) streamptr->ncmode = 1;
   if (streamptr->ncmode == 2) cdf_redef(fileID);
 
-  taxis_t *taxis = taxisPtr(vlistInqTaxis(streamptr->vlistID));
+  taxis_t *taxis = taxis_to_pointer(vlistInqTaxis(streamptr->vlistID));
 
   const char *taxisName = (taxis->name && taxis->name[0]) ? taxis->name : defaultTimeAxisName;
 
@@ -170,7 +170,8 @@ cdfDefTime(stream_t *streamptr)
   cdf_def_dim(fileID, taxisName, timeDimLen, &timeDimId);
   streamptr->basetime.ncdimid = timeDimId;
 
-  int datatype = taxis->datatype;
+  int datatype = CDI_UNDEFID;
+  cdiInqKeyInt(taxis->self, CDI_GLOBAL, CDI_KEY_DATATYPE, &datatype);
   nc_type xtype = (datatype == CDI_DATATYPE_INT32) ? NC_INT : ((datatype == CDI_DATATYPE_FLT32) ? NC_FLOAT : NC_DOUBLE);
 
   int timeVarId;
@@ -245,7 +246,7 @@ cdfDefTimestep(stream_t *streamptr, int tsID, size_t valCount)
   int time_varid = streamptr->basetime.ncvarid;
   if (time_varid != CDI_UNDEFID && tsID == 0)
     {
-      taxis_t *taxis = taxisPtr(vlistInqTaxis(streamptr->vlistID));
+      taxis_t *taxis = taxis_to_pointer(vlistInqTaxis(streamptr->vlistID));
       int fileID = streamptr->fileID;
       const char *unitstr = cdfGetTimeUnits(taxis);
       size_t len = strlen(unitstr);
diff --git a/src/taxis.c b/src/taxis.c
index 5acf45b5715e6dba9f64f41f1473927f3a56ddfd..a252db8b187e6d64c52b75b43383d3f70fe18b07 100644
--- a/src/taxis.c
+++ b/src/taxis.c
@@ -102,7 +102,6 @@ void
 ptaxisInit(taxis_t *taxisptr)
 {
   taxisptr->self = CDI_UNDEFID;
-  taxisptr->datatype = CDI_DATATYPE_FLT64;
   taxisptr->type = DefaultTimeType;
   taxisptr->calendar = CDI_Default_Calendar;
   taxisptr->unit = DefaultTimeUnit;
@@ -120,6 +119,9 @@ ptaxisInit(taxis_t *taxisptr)
   taxisptr->name = NULL;
   taxisptr->longname = NULL;
   taxisptr->units = NULL;
+
+  cdiInitKeys(&taxisptr->keys);
+  cdiDefVarKeyInt(&taxisptr->keys, CDI_KEY_DATATYPE, CDI_DATATYPE_FLT64);
 }
 
 static taxis_t *
@@ -235,7 +237,7 @@ taxisDefType(int taxisID, int taxistype)
   if (taxisptr->type != taxistype)
     {
       taxisptr->type = taxistype;
-      taxisptr->datatype = CDI_DATATYPE_FLT64;
+      cdiDefVarKeyInt(&taxisptr->keys, CDI_KEY_DATATYPE, CDI_DATATYPE_FLT64);
       delete_refcount_string(taxisptr->units);
       taxisptr->units = NULL;
       reshSetStatus(taxisID, &taxisOps, RESH_DESYNC_IN_USE);
@@ -840,7 +842,7 @@ taxisInqNumavg(int taxisID)
 }
 
 taxis_t *
-taxisPtr(int taxisID)
+taxis_to_pointer(int taxisID)
 {
   taxis_t *taxisptr = (taxis_t *) reshGetVal(taxisID, &taxisOps);
   return taxisptr;
@@ -849,7 +851,7 @@ taxisPtr(int taxisID)
 void
 ptaxisDefDatatype(taxis_t *taxisptr, int datatype)
 {
-  taxisptr->datatype = datatype;
+  cdiDefVarKeyInt(&taxisptr->keys, CDI_KEY_DATATYPE, datatype);
 }
 
 void
@@ -1349,7 +1351,6 @@ ptaxisCopy(taxis_t *dest, taxis_t *source)
   reshLock();
 
   // memcpy(dest, source, sizeof(taxis_t));
-  dest->datatype = source->datatype;
   dest->type = source->type;
   dest->calendar = source->calendar;
   dest->unit = source->unit;
@@ -1374,6 +1375,9 @@ ptaxisCopy(taxis_t *dest, taxis_t *source)
   dest->units = dup_refcount_string(source->units);
   if (dest->self != CDI_UNDEFID) reshSetStatus(dest->self, &taxisOps, RESH_DESYNC_IN_USE);
 
+  cdiInitKeys(&dest->keys);
+  cdiCopyVarKeys(&source->keys, &dest->keys);
+
   reshUnlock();
 }
 
@@ -1480,6 +1484,7 @@ taxisGetPackSize(void *p, void *context)
                        + (taxisptr->longname ? serializeGetSize((int) strlen(taxisptr->longname), CDI_DATATYPE_TXT, context) : 0)
                        + (taxisptr->units ? serializeGetSize((int) strlen(taxisptr->units), CDI_DATATYPE_TXT, context) : 0)
                        + serializeGetSize(1, CDI_DATATYPE_UINT32, context);
+  packBufferSize += serializeKeysGetPackSize(&taxisptr->keys, context);
   return packBufferSize;
 }
 
@@ -1546,6 +1551,8 @@ taxisUnpack(char *unpackBuffer, int unpackBufferSize, int *unpackBufferPos, int
       taxisP->units = units;
     }
 
+  serializeKeysUnpack(unpackBuffer, unpackBufferSize, unpackBufferPos, &taxisP->keys, context);
+
   reshSetStatus(taxisP->self, &taxisOps, reshGetStatus(taxisP->self, &taxisOps) & ~RESH_SYNC_BIT);
 #undef adaptKey
 
@@ -1596,6 +1603,8 @@ taxisPack(void *voidP, void *packBuffer, int packBufferSize, int *packBufferPos,
   if (taxisP->longname)
     serializePack(taxisP->longname, lnameLen, CDI_DATATYPE_TXT, packBuffer, packBufferSize, packBufferPos, context);
   if (taxisP->units) serializePack(taxisP->units, unitsLen, CDI_DATATYPE_TXT, packBuffer, packBufferSize, packBufferPos, context);
+
+  serializeKeysPack(&taxisP->keys, packBuffer, packBufferSize, packBufferPos, context);
 }
 
 /*
diff --git a/src/taxis.h b/src/taxis.h
index b7eac28932fc67368597f4fc030714c3782325e6..5888b513d4ba6715044d2d936faae5638f780dd4 100644
--- a/src/taxis.h
+++ b/src/taxis.h
@@ -4,6 +4,8 @@
 #include <stdbool.h>
 #include "cdi.h"
 
+#include "cdi_key.h"
+
 #ifndef RESOURCE_HANDLE_H
 #include "resource_handle.h"
 #endif
@@ -11,8 +13,7 @@
 typedef struct
 {
   int self;
-  int datatype;  // datatype
-  int type;      // time type
+  int type;  // time type
   int calendar;
   int unit;  // time units
   int numavg;
@@ -29,6 +30,7 @@ typedef struct
   char *units;
   bool climatology;
   bool hasBounds;
+  cdi_keys_t keys;
 } taxis_t;
 
 //      taxisInqSdatetime: Get the start date/time
@@ -36,7 +38,7 @@ CdiDateTime taxisInqSdatetime(int taxisID);
 
 void ptaxisInit(taxis_t *taxis);
 void ptaxisCopy(taxis_t *dest, taxis_t *source);
-taxis_t *taxisPtr(int taxisID);
+taxis_t *taxis_to_pointer(int taxisID);
 void cdi_set_forecast_period(double timevalue, taxis_t *taxis);
 CdiDateTime cdi_decode_timeval(double timevalue, const taxis_t *taxis);
 double cdi_encode_timeval(CdiDateTime datetime, taxis_t *taxis);
diff --git a/tests/test_resource_copy.c b/tests/test_resource_copy.c
index ce789c30dabe9dfd5fbf8f62c897b1481afd9b83..ffd2c7793e01a36f2764ec7b56c31b66bdd1d596 100644
--- a/tests/test_resource_copy.c
+++ b/tests/test_resource_copy.c
@@ -63,7 +63,7 @@ defineGrid(void)
   cdiDefKeyString(gridID, CDI_XAXIS, CDI_KEY_UNITS, "myXunits");
   cdiDefKeyString(gridID, CDI_YAXIS, CDI_KEY_UNITS, "myYunits");
 
-  gridDefDatatype(gridID, DOUBLE_PRECISION);
+  cdiDefKeyInt(gridID, CDI_GLOBAL, CDI_KEY_DATATYPE, DOUBLE_PRECISION);
   gridDefTrunc(gridID, 1);
   gridDefParamGME(gridID, 2, 3, 4, 5);
 
@@ -164,7 +164,7 @@ defineVlist(int gridID, int zaxisID, int taxisID)
   cdiDefAttTxt(vlistID, varID2, "txt demo", 6, "banana");
   vlistDefTaxis(vlistID, taxisID);
   int vlistID2 = vlistDuplicate(vlistID);
-  return (struct idPair){ vlistID, vlistID2 };
+  return (struct idPair) { vlistID, vlistID2 };
 }
 
 static int